feat: beta release 0.1.8

fix: algorithm time too short under some conditions
fix: countdown too short under some conditions
fix: request api stuck after some time
fix: average local HR sent not saved
feat: better manage fail of nodes from XvB
This commit is contained in:
Cyrix126 2024-04-04 19:39:55 +02:00
parent 755fe234cd
commit 70250775ec
8 changed files with 173 additions and 140 deletions

View file

@ -1,3 +1,20 @@
# v0.1.8
Fix release for beta version.
This version is only made for testing purposes and have feedback.
## Changes
### Internal
Better manage fail of nodes from XvB
## Fixes
Algorithm time too short under some conditions.
Countdown too short under some conditions
Request API stuck after some time
Average local HR sent not saved
## Bundled Versions
* [`P2Pool v3.10`](https://github.com/SChernykh/p2pool/releases/tag/v3.10)
* [`XMRig v6.21.1`](https://github.com/xmrig/xmrig/releases/tag/v6.21.1)
# v0.1.7
Fix release for beta version.
This version is only made for testing purposes and have feedback.

10
Cargo.lock generated
View file

@ -1144,9 +1144,9 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "core-graphics"
version = "0.23.1"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212"
checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
@ -2084,7 +2084,7 @@ dependencies = [
[[package]]
name = "gupaxx"
version = "0.1.7"
version = "0.1.8"
dependencies = [
"anyhow",
"benri",
@ -2131,9 +2131,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51ee2dd2e4f378392eeff5d51618cd9a63166a2513846bbc55f21cfacd9199d4"
checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069"
dependencies = [
"bytes",
"fnv",

View file

@ -1,7 +1,7 @@
cargo-features = ["profile-rustflags"]
[package]
name = "gupaxx"
version = "0.1.7"
version = "0.1.8"
authors = ["cyrix126 <gupaxx@baermail.fr>"]
description = "Fork of Gupax integrating the XMRvsBeast Raffle "
documentation = "https://github.com/cyrix126/gupaxx"

View file

@ -48,7 +48,7 @@ use std::{
time::*,
};
use self::xvb::PubXvbApi;
use self::xvb::{nodes::XvbNode, PubXvbApi};
pub mod p2pool;
pub mod tests;
pub mod xmrig;
@ -218,13 +218,13 @@ impl Default for ProcessState {
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ProcessSignal {
None,
Start,
Stop,
Restart,
UpdateNodes,
UpdateNodes(XvbNode),
}
impl Default for ProcessSignal {

View file

@ -60,11 +60,14 @@ impl Helper {
// need to verify if node still working
// for that need to catch "connect error"
if contains_connect_error(&line) {
// updating current node to None.
lock!(pub_api_xvb).current_node = None;
// send signal to update node.
warn!("XMRig PTY Parse | node is offline, switching to backup.");
lock!(process_xvb).signal = ProcessSignal::UpdateNodes;
let current_node = lock!(pub_api_xvb).current_node;
if let Some(current_node) = current_node {
// updating current node to None, will stop sending signal of FailedNode until new node is set
// send signal to update node.
warn!("XMRig PTY Parse | node is offline, sending signal to update nodes.");
lock!(process_xvb).signal = ProcessSignal::UpdateNodes(current_node);
lock!(pub_api_xvb).current_node = None;
}
}
if contains_usepool(&line) {
info!("XMRig PTY Parse | new pool detected");
@ -73,7 +76,8 @@ impl Helper {
let node = detect_new_node_xmrig(&line);
if node.is_none() {
error!("XMRig PTY Parse | node is not understood, switching to backup.");
lock!(process_xvb).signal = ProcessSignal::UpdateNodes;
// update with default will choose which XvB to prefer. Will update XvB to use p2pool.
lock!(process_xvb).signal = ProcessSignal::UpdateNodes(XvbNode::default());
}
lock!(pub_api_xvb).current_node = node;
}

View file

@ -6,7 +6,7 @@ use std::{
use log::{debug, info, warn};
use readable::num::Float;
use reqwest::Client;
use tokio::time::{sleep_until, Instant};
use tokio::time::sleep;
use crate::{
helper::{
@ -166,7 +166,6 @@ fn minimum_time_for_highest_accessible_round(st: u32, lhr: f32, chr: f32, shr: f
}
#[allow(clippy::too_many_arguments)]
async fn sleep_then_update_node_xmrig(
was_instant: Instant,
spared_time: u32,
client: &Client,
api_uri: &str,
@ -175,12 +174,12 @@ async fn sleep_then_update_node_xmrig(
gui_api_xvb: &Arc<Mutex<PubXvbApi>>,
gui_api_xmrig: &Arc<Mutex<PubXmrigApi>>,
) {
let node = lock!(gui_api_xvb).stats_priv.node.clone();
let node = lock!(gui_api_xvb).stats_priv.node;
debug!(
"Xvb Process | algo sleep for {} while mining on P2pool",
XVB_TIME_ALGO - spared_time
);
sleep_until(was_instant + Duration::from_secs((XVB_TIME_ALGO - spared_time) as u64)).await;
sleep(Duration::from_secs((XVB_TIME_ALGO - spared_time).into())).await;
// only update xmrig config if it is actually mining.
if spared_time > 0 {
debug!("Xvb Process | request xmrig to mine on XvB");
@ -215,7 +214,7 @@ async fn sleep_then_update_node_xmrig(
}
// will not quit the process until it is really done.
// xvb process watch this algo handle to see if process is finished or not.
sleep_until(was_instant + Duration::from_secs(spared_time.into())).await;
sleep(Duration::from_secs(spared_time.into())).await;
}
}
// push new value into samples before executing this calcul
@ -225,7 +224,6 @@ fn calc_last_hour_avg_hash_rate(samples: &SamplesAverageHour) -> f32 {
#[allow(clippy::too_many_arguments)]
pub(crate) async fn algorithm(
client: &Client,
last_algorithm: Instant,
gui_api_xvb: &Arc<Mutex<PubXvbApi>>,
gui_api_xmrig: &Arc<Mutex<PubXmrigApi>>,
gui_api_p2pool: &Arc<Mutex<PubP2poolApi>>,
@ -300,7 +298,6 @@ pub(crate) async fn algorithm(
// sleep 10m less spared time then request XMrig to mine on XvB
sleep_then_update_node_xmrig(
last_algorithm,
time_donated,
client,
XMRIG_CONFIG_URI,
@ -313,11 +310,14 @@ pub(crate) async fn algorithm(
lock!(gui_api_xvb)
.p2pool_sent_last_hour_samples
.0
.push_back(hashrate_xmrig * ((XVB_TIME_ALGO - time_donated) / XVB_TIME_ALGO) as f32);
.push_back(
hashrate_xmrig
* ((XVB_TIME_ALGO as f32 - time_donated as f32) / XVB_TIME_ALGO as f32),
);
lock!(gui_api_xvb)
.xvb_sent_last_hour_samples
.0
.push_back(hashrate_xmrig * (time_donated / XVB_TIME_ALGO) as f32);
.push_back(hashrate_xmrig * (time_donated as f32 / XVB_TIME_ALGO as f32));
} else {
// no share, so we mine on p2pool. We update xmrig only if it was still mining on XvB.
if lock!(gui_api_xvb).current_node != Some(XvbNode::P2pool) {
@ -345,13 +345,13 @@ pub(crate) async fn algorithm(
}
output_console(gui_api_xvb, "No share in the current PPLNS Window !");
output_console(gui_api_xvb, "Mining on P2pool for the next ten minutes.");
sleep_until(last_algorithm + Duration::from_secs(XVB_TIME_ALGO.into())).await;
sleep(Duration::from_secs(XVB_TIME_ALGO.into())).await;
lock!(gui_api_xvb)
.p2pool_sent_last_hour_samples
.0
.push_back(lock!(gui_api_xmrig).hashrate_raw_15m);
lock!(gui_api_xvb)
.p2pool_sent_last_hour_samples
.xvb_sent_last_hour_samples
.0
.push_back(0.0);
}

View file

@ -171,7 +171,7 @@ impl Helper {
// uptime of last run of algo
let last_algorithm = Arc::new(Mutex::new(tokio::time::Instant::now()));
// uptime of last request (public and private)
let mut last_request = tokio::time::Instant::now();
let last_request = Arc::new(Mutex::new(tokio::time::Instant::now()));
// algo check if his behavior must be like the first time or second time. It can reset those values to re-act like first time even if it's not the case.
let mut first_loop = true;
// retry will be accessed from the 1m spawn, it can influence the start of algo.
@ -180,7 +180,7 @@ impl Helper {
let time_donated = Arc::new(Mutex::new(0));
// let handles;
let handle_algo = Arc::new(Mutex::new(None));
let mut handle_request = None;
let handle_request = Arc::new(Mutex::new(None));
let mut msg_retry_done = false;
info!("XvB | Entering Process mode... ");
loop {
@ -217,33 +217,42 @@ impl Helper {
info!("XvB Watchdog | Signal has stopped the loop");
break;
}
let handle = lock!(handle_algo);
let is_algo_started_once = handle.is_some();
let is_algo_finished = handle.as_ref().is_some_and(|algo| algo.is_finished());
drop(handle);
let handle_algo_c = lock!(handle_algo);
let is_algo_started_once = handle_algo_c.is_some();
let is_algo_finished = handle_algo_c
.as_ref()
.is_some_and(|algo| algo.is_finished());
let handle_request_c = lock!(handle_request);
let is_request_finished = handle_request_c
.as_ref()
.is_some_and(|request: &JoinHandle<()>| request.is_finished())
|| handle_request_c.is_none();
drop(handle_algo_c);
drop(handle_request_c);
// Send an HTTP API request only if one minute is passed since the last request or if first loop or if algorithm need to retry or if request is finished and algo is finished or almost finished (only public and private stats). We make sure public and private stats are refreshed before doing another run of the algo.
// We make sure algo or request are not rerun when they are not over.
if last_request.elapsed() >= Duration::from_secs(60)
|| first_loop
|| *lock!(retry)
|| ((is_algo_finished
|| lock!(last_algorithm).elapsed()
>= Duration::from_secs((XVB_TIME_ALGO as f32 * 0.95) as u64))
&& (handle_request
.as_ref()
.is_some_and(|req: &JoinHandle<()>| req.is_finished())
|| handle_request.is_none()))
// in the case of quick refresh before new run of algo, make sure it doesn't happen multiple times.
let last_request_expired = lock!(last_request).elapsed() >= Duration::from_secs(60);
let should_refresh_before_next_algo = is_algo_started_once
&& lock!(last_algorithm).elapsed()
>= Duration::from_secs((XVB_TIME_ALGO as f32 * 0.95) as u64)
&& lock!(last_request).elapsed() >= Duration::from_secs(25);
let process_alive = lock!(process).state == ProcessState::Alive;
if ((last_request_expired || first_loop)
|| (*lock!(retry) || is_algo_finished || should_refresh_before_next_algo)
&& process_alive)
&& is_request_finished
{
// do not wait for the request to finish so that they are retrieved at exactly one minute interval and not block the thread.
// Private API will also use this instant if XvB is Alive.
last_request = tokio::time::Instant::now();
// first_loop is false here but could be changed to true under some conditions.
// will send a stop signal if public stats failed or update data with new one.
handle_request = Some(spawn(
enc!((client, pub_api, gui_api, gui_api_p2pool, gui_api_xmrig, state_xvb, state_p2pool, state_xmrig, process, last_algorithm, retry, handle_algo, time_donated) async move {
*lock!(handle_request) = Some(spawn(
enc!((client, pub_api, gui_api, gui_api_p2pool, gui_api_xmrig, state_xvb, state_p2pool, state_xmrig, process, last_algorithm, retry, handle_algo, time_donated, last_request) async move {
// needs to wait here for public stats to get private stats.
if last_request.elapsed() >= Duration::from_secs(60) || first_loop || lock!(last_algorithm).elapsed() >= Duration::from_secs((XVB_TIME_ALGO as f32 * 0.95)as u64) {
if last_request_expired || first_loop || should_refresh_before_next_algo {
XvbPubStats::update_stats(&client, &gui_api, &pub_api, &process).await;
*lock!(last_request) = Instant::now();
}
// private stats needs valid token and address.
// other stats needs everything to be alive, so just require alive here for now.
@ -253,13 +262,14 @@ impl Helper {
let share = lock!(gui_api_p2pool).sidechain_shares;
debug!("XvB | Number of current shares: {}", share);
// private stats can be requested every minute or first loop or if the have almost finished.
if last_request.elapsed() >= Duration::from_secs(60) || first_loop || lock!(last_algorithm).elapsed() >= Duration::from_secs((XVB_TIME_ALGO as f32 * 0.95)as u64) {
if last_request_expired || first_loop || should_refresh_before_next_algo {
debug!("XvB Watchdog | Attempting HTTP private API request...");
// reload private stats, it send a signal if error that will be captured on the upper thread.
XvbPrivStats::update_stats(
&client, &state_p2pool.address, &state_xvb.token, &pub_api, &gui_api, &process,
)
.await;
*lock!(last_request) = Instant::now();
// verify in which round type we are
let round = round_type(share, &pub_api);
@ -279,14 +289,9 @@ impl Helper {
*lock!(retry) = false;
// reset instant because algo will start.
*lock!(last_algorithm) = Instant::now();
// send the instant that will be consumed by algo. Algo does not modify it.
let last_algorithm = *lock!(last_algorithm);
*lock!(handle_algo) = Some(spawn(enc!((client, gui_api, gui_api_xmrig, state_xmrig, time_donated) async move {
algorithm(
&client,
last_algorithm,
&gui_api,
&gui_api_xmrig,
&gui_api_p2pool,
@ -447,7 +452,6 @@ async fn check_conditions_for_start(
ProcessState::Syncing
} else {
// all test passed, so it can be Alive
info!("XvB will ping nodes");
// stay at middle, updateNodes will finish by syncing or offlinenodes and check_status in loop will change state accordingly.
ProcessState::Middle
};
@ -460,7 +464,7 @@ async fn check_conditions_for_start(
);
}
// will update the preferred node for the first loop, even if partially started.
lock!(process_xvb).signal = ProcessSignal::UpdateNodes;
lock!(process_xvb).signal = ProcessSignal::UpdateNodes(XvbNode::default());
lock!(process_xvb).state = state;
}
#[allow(clippy::too_many_arguments)]
@ -575,85 +579,58 @@ fn signal_interrupt(
// check if STOP or RESTART Signal is given.
// if STOP, will put Signal to None, if Restart to Wait
// in either case, will break from loop.
if lock!(process).signal == ProcessSignal::Stop {
debug!("P2Pool Watchdog | Stop SIGNAL caught");
// Wait to get the exit status
let uptime = start.elapsed();
info!(
"Xvb Watchdog | Stopped ... Uptime was: [{}]",
Uptime::from(uptime)
);
// insert the signal into output of XvB
// This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it.
output_console(gui_api, "\n\n\nXvB stopped\n\n\n");
debug!("XvB Watchdog | Stop SIGNAL done, breaking");
lock!(process).signal = ProcessSignal::None;
lock!(process).state = ProcessState::Dead;
// reset stats
reset_data_xvb(pub_api, gui_api);
return true;
// Check RESTART
} else if lock!(process).signal == ProcessSignal::Restart {
debug!("XvB Watchdog | Restart SIGNAL caught");
let uptime = Uptime::from(start.elapsed());
info!("XvB Watchdog | Stopped ... Uptime was: [{}]", uptime);
// no output to console because service will be started with fresh output.
debug!("XvB Watchdog | Restart SIGNAL done, breaking");
lock!(process).state = ProcessState::Waiting;
reset_data_xvb(pub_api, gui_api);
return true;
// Check UPDATE NODES
} else if lock!(process).signal == ProcessSignal::UpdateNodes
&& lock!(process).state != ProcessState::Waiting
{
info!("XvB Watchdog | Signal has been given to ping and reselect Nodes.");
// if signal is waiting, he is restarting or already updating nodes.
// need to know if it was starting xvb
// A signal has been given to ping the nodes and select the fastest.
let token_xmrig = state_xmrig.token.clone();
let address = state_p2pool.address.clone();
let alive = lock!(process).state == ProcessState::Alive;
// so it won't execute another signal of update nodes if it is already doing it.
lock!(process).state = ProcessState::Waiting;
lock!(process).signal = ProcessSignal::None;
spawn(
enc!((gui_api, pub_api, gui_api_xmrig, client, process) async move {
// if nodes die while being used by xmrig, it needs see which one is capable, send a signal if none is useable and switch xmrig to the good node (or p2pool if none).
// update nodes will make the state Syncing, and the loop of the thread will detected if it can be made alive.
XvbNode::update_fastest_node(&client, &gui_api, &pub_api, &process).await;
// only update xmrig if state is alive.
if alive {
let node = lock!(gui_api).stats_priv.node.clone();
if let Err(err) = PrivXmrigApi::update_xmrig_config(
&client,
XMRIG_CONFIG_URI,
&token_xmrig,
&node,
&address,
&gui_api_xmrig,
)
.await
{
// show to console error about updating xmrig config
output_console(
&gui_api,
&format!(
"Failure to update xmrig config with HTTP API.\nError: {}",
err
),
);
} else {
output_console(
&gui_api,
&format!("XvB node failed, falling back to {}", node),
);
}
}
// verify if nodes were joignable.
if lock!(process).state == ProcessState::OfflineNodesAll {
// if state did not change after updating fastest node, it means all xvb nodes failed.
// we need to spawn a process to verify periodiccly until the nodes are online. The service state will be Alive then.
let signal = lock!(process).signal;
match signal {
ProcessSignal::Stop => {
debug!("P2Pool Watchdog | Stop SIGNAL caught");
// Wait to get the exit status
let uptime = start.elapsed();
info!(
"Xvb Watchdog | Stopped ... Uptime was: [{}]",
Uptime::from(uptime)
);
// insert the signal into output of XvB
// This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it.
output_console(gui_api, "\n\n\nXvB stopped\n\n\n");
debug!("XvB Watchdog | Stop SIGNAL done, breaking");
lock!(process).signal = ProcessSignal::None;
lock!(process).state = ProcessState::Dead;
// reset stats
reset_data_xvb(pub_api, gui_api);
return true;
}
ProcessSignal::Restart => {
debug!("XvB Watchdog | Restart SIGNAL caught");
let uptime = Uptime::from(start.elapsed());
info!("XvB Watchdog | Stopped ... Uptime was: [{}]", uptime);
// no output to console because service will be started with fresh output.
debug!("XvB Watchdog | Restart SIGNAL done, breaking");
lock!(process).state = ProcessState::Waiting;
reset_data_xvb(pub_api, gui_api);
return true;
}
ProcessSignal::UpdateNodes(node) => {
if lock!(process).state != ProcessState::Waiting {
let token_xmrig = state_xmrig.token.clone();
let address = state_p2pool.address.clone();
// check if state is alive. If it is and it is receiving such a signal, it means something a node (XvB or P2Pool) has failed.
// if XvB, xmrig needs to be switch to the other node (both will be checked though to be sure).
// if both XvB nodes fail after checking, process will be partially stopped and a new spawn will verify if nodes are again online and so will continue the process completely if that's the case.
// if P2pool, the process has to stop the algo and continue partially. The process will continue completely if the confitions are met again.
// if XvB was not alive, then if it is for XvB nodes, it will check and update preferred node and set XMRig to P2pool if that's not the case.
// if XvB was not alive and update was for P2pool, XvB must ignore. XMRig will stop sending signals because current node will be none.
let was_alive = lock!(process).state != ProcessState::Alive;
// so it won't execute another signal of update nodes if it is already doing it.
lock!(process).state = ProcessState::Waiting;
lock!(process).signal = ProcessSignal::None;
spawn(
enc!((node, process, client, gui_api, pub_api, was_alive, address, token_xmrig, gui_api_xmrig) async move {
match node {
XvbNode::NorthAmerica|XvbNode::Europe if was_alive => {
// a node is failing. We need to first verify if a node is available
XvbNode::update_fastest_node(&client, &gui_api, &pub_api, &process).await;
if lock!(process).state == ProcessState::OfflineNodesAll {
// No available nodes, so launch a process to verify periodicly.
sleep(Duration::from_secs(10)).await;
info!("node fail, set spawn that will retry nodes and update state.");
while lock!(process).state == ProcessState::OfflineNodesAll {
@ -661,12 +638,47 @@ fn signal_interrupt(
XvbNode::update_fastest_node(&client, &pub_api, &gui_api, &process).await;
sleep(Duration::from_secs(10)).await;
}
}
}),
);
// the state will be Offline or Alive after update_fastest_node is done, meanwhile Signal will be None so not re-treated before update_fastest is done.
// so if a backup was used, it will be alive. If not, the algorithm stop and xmrig is updated to mine on p2pool.
}
// a good node is found, so the next check of the loop should be good and the algo will update XMRig with the good one.
},
XvbNode::NorthAmerica|XvbNode::Europe if !was_alive => {
// Probably a start. We don't consider XMRig using XvB nodes without algo.
// can update xmrig and check status of state in the same time.
// Need to set XMRig to P2Pool if it wasn't. XMRig should have populated this value at his start.
if lock!(gui_api).current_node != Some(XvbNode::P2pool) {
spawn(enc!((client, token_xmrig, address, gui_api_xmrig, gui_api) async move{
if let Err(err) = PrivXmrigApi::update_xmrig_config(
&client,
XMRIG_CONFIG_URI,
&token_xmrig,
&XvbNode::P2pool,
&address,
&gui_api_xmrig,
)
.await {
output_console(
&gui_api,
&format!(
"Failure to update xmrig config with HTTP API.\nError: {}",
err
),
);
}
}
));}
},
_ => {}
} } ),
);
}
}
_ => {}
}
false
}
fn reset_data_xvb(pub_api: &Arc<Mutex<PubXvbApi>>, gui_api: &Arc<Mutex<PubXvbApi>>) {
@ -706,7 +718,7 @@ fn update_indicator_algo(
last_algorithm: &Arc<Mutex<Instant>>,
) {
if is_algo_started_once && !is_algo_finished && lock!(process).state == ProcessState::Alive {
let node = lock!(pub_api).current_node.clone();
let node = lock!(pub_api).current_node;
let msg_indicator = match node {
Some(XvbNode::P2pool) if time_donated > 0 => {
// algo is mining on p2pool but will switch to XvB after

View file

@ -16,7 +16,7 @@ use crate::{
};
use super::PubXvbApi;
#[derive(Clone, Debug, Default, PartialEq, Display)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Display)]
pub enum XvbNode {
#[display(fmt = "XvB North America Node")]
NorthAmerica,