feat: integrate algorithm of HR distribution

This commit is contained in:
Louis-Marie Baer 2024-03-17 01:15:08 +01:00
parent 87eb738ca8
commit 74c6a92535
10 changed files with 496 additions and 123 deletions

View file

@ -64,7 +64,7 @@ rand = "0.8.5"
regex = { version = "1.10.3", default-features = false, features = ["perf"] }
rfd = "0.14.0"
serde = { version = "1.0.197", features = ["rc", "derive"] }
serde_json = "1.0"
serde_json = "1.0.114"
sysinfo = { version = "0.30.5", default-features = false }
tls-api = "0.9.0"
tokio = { version = "1.36.0", features = ["rt", "time", "macros", "process"] }

View file

@ -71,6 +71,27 @@ The mHR is calculated depending on the sidechain the p2pool is mining on.
The XvB process will check every ten minutes the last 15 minutes average HR and decide when to switch (in seconds) for the ten next minutes. (first p2pool then XvB).
*Need to see the time for Xmrig takes to set the new settings by API.*
When the time to switch arrives, XvB process will send a request to Xmrig to change the node used.
### Modification of config of xmrig
The following 4 attributes must be applied to xmrig config when mining to XvB node.
```ignore
"url": "xvb node:4247"
"user": "user id",
"keepalive": true,
"tls": true,
```
Or to return back to p2pool
```ignore
"url": "127.0.0.1:3333"
"user": "Gupax_v1_3_5",
"keepalive": false,
"tls": false,
```
The HTTP API of xmrig requires to give a full config.
The current config will be requested, modified and sent back.
[^1]: https://p2pool.io/mini/api/pool/stats
[^2]: https://github.com/SChernykh/p2pool?tab=readme-ov-file#how-payouts-work-in-p2pool

View file

@ -535,7 +535,12 @@ impl crate::app::App {
.on_hover_text("Restart Xvb")
.clicked()
{
Helper::restart_xvb(&self.helper, &self.state.xvb, &self.state.p2pool);
Helper::restart_xvb(
&self.helper,
&self.state.xvb,
&self.state.p2pool,
&self.state.xmrig,
);
}
if key.is_down() && !wants_input
|| ui
@ -569,7 +574,12 @@ impl crate::app::App {
.on_disabled_hover_text(XVB_NOT_CONFIGURED)
.clicked()
{
Helper::start_xvb(&self.helper, &self.state.xvb, &self.state.p2pool);
Helper::start_xvb(
&self.helper,
&self.state.xvb,
&self.state.p2pool,
&self.state.xmrig,
);
}
}
});

View file

@ -161,7 +161,7 @@ path_xmr: {:#?}\n
}
Tab::Xvb => {
debug!("App | Entering [XvB] Tab");
crate::disk::state::Xvb::show(&mut self.state.xvb, self.size, &self.state.p2pool.address, ctx, ui, &self.xvb_api, lock!(self.xvb).is_alive());
crate::disk::state::Xvb::show(&mut self.state.xvb, self.size, &self.state.p2pool.address, ctx, ui, &self.xvb_api, lock!(self.xvb).is_alive()&& !lock!(self.xvb).is_syncing() && !lock!(self.xvb).is_not_mining());
}
}
});

View file

@ -3,6 +3,7 @@ use std::sync::{Arc, Mutex};
use egui::TextStyle::{self, Name};
use egui::{vec2, Hyperlink, Image, RichText, TextEdit, Ui, Vec2};
use log::debug;
use readable::byte::Byte;
use crate::helper::xvb::PubXvbApi;
use crate::utils::constants::{
@ -26,7 +27,7 @@ impl crate::disk::state::Xvb {
_ctx: &egui::Context,
ui: &mut egui::Ui,
api: &Arc<Mutex<PubXvbApi>>,
xvb_is_alive: bool,
private_stats: bool,
) {
let website_height = size.y / 10.0;
let width = size.x;
@ -104,75 +105,90 @@ impl crate::disk::state::Xvb {
}
// private stats
let priv_stats = &lock!(api).stats_priv;
ui.set_enabled(xvb_is_alive);
// ui.vertical_centered(|ui| {
ui.add_space(SPACE * 2.0);
ui.horizontal(|ui| {
// widget takes a third less space for two separator.
let width_stat = (ui.available_width() / 5.0)
- ((24.0 + ui.style().spacing.item_spacing.x + SPACE) / 5.0);
// 0.0 means minimum
let height_stat = 0.0;
let size_stat = vec2(width_stat, height_stat);
let round = match &priv_stats.round_participate {
Some(r) => r.to_string(),
None => "None".to_string(),
};
ui.add_sized(size_stat, |ui: &mut Ui| {
ui.group(|ui| {
let size_stat = vec2(
ui.available_width(),
0.0, // + ui.spacing().item_spacing.y,
);
ui.add_sized(size_stat, |ui: &mut Ui| {
ui.vertical_centered(|ui| {
ui.label(XVB_FAILURE_FIELD);
ui.label(priv_stats.fails.to_string());
})
.response
});
ui.separator();
ui.add_sized(size_stat, |ui: &mut Ui| {
ui.vertical_centered(|ui| {
ui.label(XVB_DONATED_1H_FIELD);
ui.label(priv_stats.donor_1hr_avg.to_string());
})
.response
});
ui.separator();
ui.add_sized(size_stat, |ui: &mut Ui| {
ui.vertical_centered(|ui| {
ui.label(XVB_DONATED_24H_FIELD);
ui.label(priv_stats.donor_24hr_avg.to_string());
})
.response
});
ui.separator();
ui.add_enabled_ui(priv_stats.round_participate.is_some(), |ui| {
ui.add_enabled_ui(private_stats, |ui| {
ui.add_space(SPACE * 2.0);
ui.horizontal(|ui| {
// widget takes a third less space for two separator.
let width_stat = (ui.available_width() / 5.0)
- ((24.0 + ui.style().spacing.item_spacing.x + SPACE) / 5.0);
// 0.0 means minimum
let height_stat = 0.0;
let size_stat = vec2(width_stat, height_stat);
let round = match &priv_stats.round_participate {
Some(r) => r.to_string(),
None => "None".to_string(),
};
ui.add_sized(size_stat, |ui: &mut Ui| {
ui.group(|ui| {
let size_stat = vec2(
ui.available_width(),
0.0, // + ui.spacing().item_spacing.y,
);
ui.add_sized(size_stat, |ui: &mut Ui| {
ui.vertical_centered(|ui| {
ui.label(XVB_ROUND_TYPE_FIELD);
ui.label(round);
ui.label(XVB_FAILURE_FIELD);
ui.label(priv_stats.fails.to_string());
})
.response
})
.on_disabled_hover_text("You do not yet have a share in the PPLNS Window.");
});
ui.separator();
ui.add_sized(size_stat, |ui: &mut Ui| {
ui.vertical_centered(|ui| {
ui.label(XVB_WINNER_FIELD);
ui.label(
priv_stats
.win_current
.then(|| "You are Winning the round !")
.unwrap_or("You are not the winner"),
});
ui.separator();
ui.add_sized(size_stat, |ui: &mut Ui| {
ui.vertical_centered(|ui| {
ui.label(XVB_DONATED_1H_FIELD);
ui.label(
[
Byte::from(priv_stats.donor_1hr_avg).to_string(),
"H/s".to_string(),
]
.concat(),
);
})
.response
});
ui.separator();
ui.add_sized(size_stat, |ui: &mut Ui| {
ui.vertical_centered(|ui| {
ui.label(XVB_DONATED_24H_FIELD);
ui.label(
[
Byte::from(priv_stats.donor_24hr_avg).to_string(),
"H/s".to_string(),
]
.concat(),
);
})
.response
});
ui.separator();
ui.add_enabled_ui(priv_stats.round_participate.is_some(), |ui| {
ui.add_sized(size_stat, |ui: &mut Ui| {
ui.vertical_centered(|ui| {
ui.label(XVB_ROUND_TYPE_FIELD);
ui.label(round);
})
.response
})
.on_disabled_hover_text(
"You do not yet have a share in the PPLNS Window.",
);
})
.response
});
})
.response
});
ui.separator();
ui.add_sized(size_stat, |ui: &mut Ui| {
ui.vertical_centered(|ui| {
ui.label(XVB_WINNER_FIELD);
ui.label(
priv_stats
.win_current
.then(|| "You are Winning the round !")
.unwrap_or("You are not the winner"),
);
})
.response
});
})
.response
});
});
});
// Rules link help

View file

@ -255,6 +255,37 @@ pub enum XvbNode {
NorthAmerica,
#[default]
Europe,
P2pool,
}
impl XvbNode {
pub fn url(&self) -> String {
match self {
Self::NorthAmerica => String::from(XVB_NODE_NA),
Self::Europe => String::from(XVB_NODE_EU),
Self::P2pool => String::from("127.0.0.1:3333"),
}
}
pub fn user(&self, address: &str) -> String {
match self {
Self::NorthAmerica => address.chars().take(8).collect(),
Self::Europe => address.chars().take(8).collect(),
Self::P2pool => GUPAX_VERSION_UNDERSCORE.to_string(),
}
}
pub fn tls(&self) -> bool {
match self {
Self::NorthAmerica => true,
Self::Europe => true,
Self::P2pool => false,
}
}
pub fn keepalive(&self) -> bool {
match self {
Self::NorthAmerica => true,
Self::Europe => true,
Self::P2pool => false,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]

View file

@ -1,12 +1,15 @@
use crate::disk::state::XvbNode;
use crate::helper::{ProcessName, ProcessSignal, ProcessState};
use crate::regex::XMRIG_REGEX;
use crate::utils::human::HumanNumber;
use crate::utils::sudo::SudoState;
use crate::{constants::*, macros::*};
use anyhow::{anyhow, Result};
use log::*;
use readable::num::Unsigned;
use readable::up::Uptime;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::path::Path;
use std::{
fmt::Write,
@ -199,8 +202,6 @@ impl Helper {
args.push("127.0.0.1".to_string()); // HTTP API IP
args.push("--http-port".to_string());
args.push("18088".to_string()); // HTTP API Port
args.push(format!("--http-access-token={}", state.token)); // HTTP API Port
args.push("--http-no-restricted".to_string());
if state.pause != 0 {
args.push("--pause-on-active".to_string());
args.push(state.pause.to_string());
@ -290,6 +291,8 @@ impl Helper {
};
}
}
args.push(format!("--http-access-token={}", state.token)); // HTTP API Port
args.push("--http-no-restricted".to_string());
(args, format!("{}:{}", api_ip, api_port))
}
@ -622,8 +625,8 @@ pub struct PubXmrigApi {
pub diff: String,
pub accepted: String,
pub rejected: String,
pub hashrate_raw: f32,
pub hashrate_raw_15m: f32,
}
impl Default for PubXmrigApi {
@ -644,6 +647,7 @@ impl PubXmrigApi {
accepted: UNKNOWN_DATA.to_string(),
rejected: UNKNOWN_DATA.to_string(),
hashrate_raw: 0.0,
hashrate_raw_15m: 0.0,
}
}
@ -701,6 +705,10 @@ impl PubXmrigApi {
Some(Some(h)) => *h,
_ => 0.0,
};
let hashrate_raw_15m = match private.hashrate.total.last() {
Some(Some(h)) => *h,
_ => 0.0,
};
*public = Self {
worker_id: private.worker_id,
@ -710,6 +718,7 @@ impl PubXmrigApi {
accepted: Unsigned::from(private.connection.accepted as usize).to_string(),
rejected: Unsigned::from(private.connection.rejected as usize).to_string(),
hashrate_raw,
hashrate_raw_15m,
..std::mem::take(&mut *public)
}
}
@ -758,6 +767,61 @@ impl PrivXmrigApi {
let body = hyper::body::to_bytes(response?.body_mut()).await?;
Ok(serde_json::from_slice::<Self>(&body)?)
}
// #[inline]
// // Replace config with new node
pub async fn update_xmrig_config(
client: &hyper::Client<hyper::client::HttpConnector>,
api_uri: &str,
token: &str,
node: XvbNode,
address: &str,
) -> Result<()> {
// get config
let request = hyper::Request::builder()
.method("GET")
.header("Authorization", ["Bearer ", token].concat())
.uri(api_uri)
.body(hyper::Body::empty())?;
let response = tokio::time::timeout(
std::time::Duration::from_millis(500),
client.request(request),
)
.await?;
let body = hyper::body::to_bytes(response?.body_mut()).await?;
// deserialize to json
let mut config = serde_json::from_slice::<Value>(&body)?;
// modify node configuration
*config
.pointer_mut("/pools/0/url")
.ok_or_else(|| anyhow!("pools/0/url does not exist in xmrig config"))? =
node.url().into();
*config
.pointer_mut("/pools/0/user")
.ok_or_else(|| anyhow!("pools/0/user does not exist in xmrig config"))? =
node.user(&address).into();
*config
.pointer_mut("/pools/0/tls")
.ok_or_else(|| anyhow!("pools/0/tls does not exist in xmrig config"))? =
node.tls().into();
*config
.pointer_mut("/pools/0/keepalive")
.ok_or_else(|| anyhow!("pools/0/keepalive does not exist in xmrig config"))? =
node.keepalive().into();
// reconstruct body from new config
let body = hyper::body::Body::from(config.to_string());
// send new config
let request = hyper::Request::builder()
.method("PUT")
.header("Authorization", ["Bearer ", token].concat())
.uri(api_uri)
.body(body)?;
tokio::time::timeout(
std::time::Duration::from_millis(500),
client.request(request),
)
.await??;
anyhow::Ok(())
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]

View file

@ -2,19 +2,28 @@ use anyhow::{bail, Result};
use bytes::Bytes;
use derive_more::Display;
use hyper::client::HttpConnector;
use hyper::StatusCode;
use hyper::{Client, StatusCode};
use hyper_tls::HttpsConnector;
use log::{debug, error, info, warn};
use readable::up::Uptime;
use serde::Deserialize;
use std::fmt::Write;
use std::time::Duration;
use std::{
sync::{Arc, Mutex},
thread,
time::Instant,
};
use tokio::spawn;
use tokio::time::sleep_until;
use crate::utils::constants::{XVB_PUBLIC_ONLY, XVB_URL};
use crate::disk::state::XvbNode;
use crate::helper::xmrig::PrivXmrigApi;
use crate::utils::constants::{
BLOCK_PPLNS_WINDOW_MAIN, BLOCK_PPLNS_WINDOW_MINI, SECOND_PER_BLOCK_P2POOL, XMRIG_CONFIG_URI,
XVB_BUFFER, XVB_PUBLIC_ONLY, XVB_ROUND_DONOR_MEGA_MIN_HR, XVB_ROUND_DONOR_MIN_HR,
XVB_ROUND_DONOR_VIP_MIN_HR, XVB_ROUND_DONOR_WHALE_MIN_HR, XVB_TIME_ALGO, XVB_URL,
};
use crate::{
helper::{ProcessSignal, ProcessState},
utils::{
@ -24,6 +33,7 @@ use crate::{
};
use super::p2pool::PubP2poolApi;
use super::xmrig::PubXmrigApi;
use super::{Helper, Process};
impl Helper {
@ -37,6 +47,7 @@ impl Helper {
helper: &Arc<Mutex<Self>>,
state_xvb: &crate::disk::state::Xvb,
state_p2pool: &crate::disk::state::P2pool,
state_xmrig: &crate::disk::state::Xmrig,
) {
info!("XvB | Attempting to restart...");
lock2!(helper, xvb).signal = ProcessSignal::Restart;
@ -44,6 +55,7 @@ impl Helper {
let helper = helper.clone();
let state_xvb = state_xvb.clone();
let state_p2pool = state_p2pool.clone();
let state_xmrig = state_xmrig.clone();
// This thread lives to wait, start xmrig then die.
thread::spawn(move || {
while lock2!(helper, xvb).state != ProcessState::Waiting {
@ -52,7 +64,7 @@ impl Helper {
}
// Ok, process is not alive, start the new one!
info!("XvB | Old process seems dead, starting new one!");
Self::start_xvb(&helper, &state_xvb, &state_p2pool);
Self::start_xvb(&helper, &state_xvb, &state_p2pool, &state_xmrig);
});
info!("XMRig | Restart ... OK");
}
@ -60,6 +72,7 @@ impl Helper {
helper: &Arc<Mutex<Self>>,
state_xvb: &crate::disk::state::Xvb,
state_p2pool: &crate::disk::state::P2pool,
state_xmrig: &crate::disk::state::Xmrig,
) {
info!("XvB | setting state to Middle");
lock2!(helper, xvb).state = ProcessState::Middle;
@ -70,6 +83,8 @@ impl Helper {
// needed to see if it is alive. For XvB process to function completely, p2pool node must be alive to check the shares in the pplns window.
let process_p2pool = Arc::clone(&lock!(helper).p2pool);
let gui_api_p2pool = Arc::clone(&lock!(helper).gui_api_p2pool);
let process_xmrig = Arc::clone(&lock!(helper).xmrig);
let gui_api_xmrig = Arc::clone(&lock!(helper).gui_api_xmrig);
info!("XvB | cloning of state");
// Reset before printing to output.
// Need to reset because values of stats would stay otherwise which could bring confusion even if panel is with a disabled theme.
@ -87,6 +102,7 @@ impl Helper {
// verify if token and address are existent on XvB server
let state_xvb = state_xvb.clone();
let state_p2pool = state_p2pool.clone();
let state_xmrig = state_xmrig.clone();
info!("XvB | spawn watchdog");
thread::spawn(move || {
@ -96,8 +112,11 @@ impl Helper {
process,
&state_xvb,
&state_p2pool,
&state_xmrig,
gui_api_p2pool,
process_p2pool,
gui_api_xmrig,
process_xmrig,
);
});
}
@ -108,33 +127,29 @@ impl Helper {
process: Arc<Mutex<Process>>,
state_xvb: &crate::disk::state::Xvb,
state_p2pool: &crate::disk::state::P2pool,
state_xmrig: &crate::disk::state::Xmrig,
gui_api_p2pool: Arc<Mutex<PubP2poolApi>>,
process_p2pool: Arc<Mutex<Process>>,
gui_api_xmrig: Arc<Mutex<PubXmrigApi>>,
process_xmrig: Arc<Mutex<Process>>,
) {
let https = HttpsConnector::new();
let client = hyper::Client::builder().build(https);
let resp =
XvbPrivStats::request_api(&client, &state_p2pool.address, &state_xvb.token).await;
info!("XvB | verify address and token");
match resp {
Ok(_) => {
let mut lock = lock!(process);
lock.state = ProcessState::Alive;
}
Err(err) => {
// send to console: token non existent for address on XvB server
warn!("Xvb | Start ... Partially failed because token and associated address are not existent on XvB server: {}\n", err);
// output the error to console
if let Err(e) = writeln!(
if let Err(err) =
XvbPrivStats::request_api(&client, &state_p2pool.address, &state_xvb.token).await
{
// send to console: token non existent for address on XvB server
warn!("Xvb | Start ... Partially failed because token and associated address are not existent on XvB server: {}\n", err);
// output the error to console
if let Err(e) = writeln!(
lock!(gui_api).output,
"Failure to retrieve private stats from XvB server.\nError: {}\n{}\n",
"Token and associated address are not valid on XvB API.\nCheck if you are registered.\nError: {}\n",
err,
XVB_PUBLIC_ONLY
) {
error!("XvB Watchdog | GUI status write failed: {}", e);
}
lock!(process).state = ProcessState::NotMining;
}
lock!(process).state = ProcessState::NotMining;
}
info!("XvB | verify p2pool node");
if !lock!(process_p2pool).is_alive() {
@ -143,7 +158,21 @@ impl Helper {
// output the error to console
if let Err(e) = writeln!(
lock!(gui_api).output,
"Failure to completely start XvB process because p2pool instance is not running.\n",
"P2pool process is not running.\nCheck the P2pool Tab\n",
) {
error!("XvB Watchdog | GUI status write failed: {}", e);
}
lock!(process).state = ProcessState::Syncing;
}
if !lock!(process_xmrig).is_alive() {
// send to console: p2pool process is not running
warn!("Xvb | Start ... Partially failed because Xmrig instance is not running.");
// output the error to console
if let Err(e) = writeln!(
lock!(gui_api).output,
"XMRig process is not running.\nCheck the Xmrig Tab.\n",
) {
error!("XvB Watchdog | GUI status write failed: {}", e);
}
@ -151,12 +180,17 @@ impl Helper {
lock!(process).state = ProcessState::Syncing;
}
info!("XvB | print to console state");
if lock!(process).state != ProcessState::Alive {
if let Err(e) = writeln!(lock!(gui_api).output, "{}\n", XVB_PUBLIC_ONLY,) {
if lock!(process).state != ProcessState::Middle {
if let Err(e) = writeln!(
lock!(gui_api).output,
"XvB partially started.\n{}\n",
XVB_PUBLIC_ONLY,
) {
error!("XvB Watchdog | GUI status write failed: {}", e);
}
} else {
info!("XvB started");
info!("XvB Fully started");
lock!(process).state = ProcessState::Alive;
if let Err(e) = writeln!(lock!(gui_api).output, "XvB started\n") {
error!("XvB Watchdog | GUI status write failed: {}", e);
}
@ -177,19 +211,23 @@ impl Helper {
// let mut old_shares = 0;
let mut time_last_share: Option<Instant> = None;
let start = lock!(process).start;
let mut start_algorithm = tokio::time::Instant::now();
info!("XvB | Entering watchdog mode... woof!");
loop {
debug!("XvB Watchdog | ----------- Start of loop -----------");
// verify if p2pool node is running with correct token.
// if address and token valid, verify if p2pool and xmrig are running, else XvBmust be reloaded with another token/address to start verifying the other process.
if lock!(process).state != ProcessState::NotMining {
if lock!(process_p2pool).is_alive() {
// verify if p2pool node and xmrig are running
if lock!(process_p2pool).is_alive() && lock!(process_xmrig).is_alive() {
// verify if state is to changed
if lock!(process).state == ProcessState::Syncing {
info!("XvB | started this time with p2pool");
info!("XvB | started this time with p2pool and xmrig");
*lock!(pub_api) = PubXvbApi::new();
*lock!(gui_api) = PubXvbApi::new();
lock!(process).state = ProcessState::Alive;
if let Err(e) = writeln!(
lock!(gui_api).output,
"XvB is now started because p2pool node came online.\n",
"XvB is now started because p2pool and xmrig came online.\n",
) {
error!("XvB Watchdog | GUI status write failed: {}", e);
}
@ -198,14 +236,15 @@ impl Helper {
// verify if the state is changing because p2pool is not alive anymore.
if lock!(process).state != ProcessState::Syncing {
info!("XvB | stop partially because p2pool is not alive anymore.");
lock!(process).state = ProcessState::Alive;
*lock!(pub_api) = PubXvbApi::new();
*lock!(gui_api) = PubXvbApi::new();
lock!(process).state = ProcessState::Syncing;
if let Err(e) = writeln!(
lock!(gui_api).output,
"XvB is now partially stopped because p2pool node came offline.\n",
"XvB is now partially stopped because p2pool node or xmrig came offline.\nCheck P2pool and Xmrig Tabs",
) {
error!("XvB Watchdog | GUI status write failed: {}", e);
}
lock!(process).state = ProcessState::Syncing
}
}
}
@ -307,13 +346,27 @@ impl Helper {
let round = if share {
let stats_priv = &lock!(pub_api).stats_priv;
match (
stats_priv.donor_1hr_avg / 1000.0,
stats_priv.donor_24hr_avg / 1000.0,
stats_priv.donor_1hr_avg as u32,
stats_priv.donor_24hr_avg as u32,
) {
x if x.0 > 1000.0 && x.1 > 1000.0 => Some(XvbRound::DonorMega),
x if x.0 > 100.0 && x.1 > 100.0 => Some(XvbRound::DonorWhale),
x if x.0 > 10.0 && x.1 > 10.0 => Some(XvbRound::DonorVip),
x if x.0 > 1.0 && x.1 > 1.0 => Some(XvbRound::Donor),
x if x.0 > XVB_ROUND_DONOR_MEGA_MIN_HR
&& x.1 > XVB_ROUND_DONOR_MEGA_MIN_HR =>
{
Some(XvbRound::DonorMega)
}
x if x.0 > XVB_ROUND_DONOR_WHALE_MIN_HR
&& x.1 > XVB_ROUND_DONOR_WHALE_MIN_HR =>
{
Some(XvbRound::DonorWhale)
}
x if x.0 > XVB_ROUND_DONOR_VIP_MIN_HR
&& x.1 > XVB_ROUND_DONOR_VIP_MIN_HR =>
{
Some(XvbRound::DonorVip)
}
x if x.0 > XVB_ROUND_DONOR_MIN_HR && x.1 > XVB_ROUND_DONOR_MIN_HR => {
Some(XvbRound::Donor)
}
(_, _) => Some(XvbRound::Vip),
}
} else {
@ -328,20 +381,83 @@ impl Helper {
{
lock!(pub_api).stats_priv.win_current = true
}
// if 10 minutes passed since last check
if lock!(gui_api).tick_distribute_hr > (60 * 10) {
// the first 15 minutes, the HR of xmrig will be 0.0, so xmrig will always mine on p2pool for 15m.
if start_algorithm.elapsed() >= Duration::from_secs(XVB_TIME_ALGO.into()) {
info!("Xvb Process | Algorithm is started");
// the time that takes the algorithm do decide the next ten minutes could means less p2pool mining. It is solved by the buffer.
start_algorithm = tokio::time::Instant::now();
// request XMrig to mine on P2pool
info!("Xvb Process | request to mine on p2pool");
let client: hyper::Client<hyper::client::HttpConnector> =
hyper::Client::builder().build(hyper::client::HttpConnector::new());
let api_uri = ["http://127.0.0.1:18088/", XMRIG_CONFIG_URI].concat();
if let Err(err) = PrivXmrigApi::update_xmrig_config(
&client,
&api_uri,
&state_xmrig.token,
XvbNode::P2pool,
&state_p2pool.address,
)
.await
{
// show to console error about updating xmrig config
if let Err(e) = writeln!(
lock!(gui_api).output,
"Failure to update xmrig config with HTTP API.\nError: {}",
err
) {
error!("XvB Watchdog | GUI status write failed: {}", e);
}
}
// if share is in PW,
if share {
info!("Xvb Process | Algorithm share is in current window");
// calcul minimum HR
let hr = lock!(gui_api_xmrig).hashrate_raw_15m;
let min_hr = Helper::minimum_hashrate_share(
lock!(gui_api_p2pool).p2pool_difficulty_u64,
state_p2pool.mini,
);
info!("Xvb Process | hr {}, min_hr: {} ", hr, min_hr);
// calculate how much time can be spared
let mut spared_time = Helper::time_that_could_be_spared(hr, min_hr);
if spared_time > 0 {
// if not hero option
if !state_xvb.hero {
// calculate how much time needed to be spared to be in most round type minimum HR + buffer
spared_time = Helper::minimum_time_for_highest_accessible_round(
spared_time,
hr,
);
}
info!("Xvb Process | spared time {} ", spared_time);
// sleep 10m less spared time then request XMrig to mine on XvB
let was_instant = start_algorithm.clone();
let node = state_xvb.node.clone();
let token = state_xmrig.token.clone();
let address = state_p2pool.address.clone();
let gui_api = gui_api.clone();
spawn(async move {
Helper::sleep_then_update_node_xmrig(
was_instant,
spared_time,
&client,
&api_uri,
&token,
node,
&address,
gui_api,
)
.await;
});
}
}
}
// if share is in PW,
// check average HR of last 15 minutes from XMrig.
// if HR + buffer >= mHR,
// calculate how much time can be spared
// if not hero option
// calculate how much time needed to be spared to be in most round type minimum HR + buffer
// fi
// sleep 10m less spared time then request XMrig to mine on XvB
// fi
// fi
// instant saved for next check
// fi
}
@ -364,6 +480,61 @@ impl Helper {
}
}
}
fn minimum_hashrate_share(difficulty: u64, mini: bool) -> f32 {
let pws = if mini {
BLOCK_PPLNS_WINDOW_MINI
} else {
BLOCK_PPLNS_WINDOW_MAIN
};
(difficulty / (pws * SECOND_PER_BLOCK_P2POOL)) as f32 * XVB_BUFFER
}
fn time_that_could_be_spared(hr: f32, min_hr: f32) -> u32 {
// percent of time minimum
let minimum_time_required_on_p2pool = XVB_TIME_ALGO as f32 / (hr / min_hr);
let spared_time = XVB_TIME_ALGO as f32 - minimum_time_required_on_p2pool;
// if less than 10 seconds, XMRig could hardly have the time to mine anything.
if spared_time >= 10f32 {
return spared_time as u32;
}
0
}
fn minimum_time_for_highest_accessible_round(st: u32, hr: f32) -> u32 {
let hr_for_xvb = ((st as f32 / XVB_TIME_ALGO as f32) * hr) as u32;
match hr_for_xvb {
x if x > XVB_ROUND_DONOR_MEGA_MIN_HR => x - XVB_ROUND_DONOR_MEGA_MIN_HR,
x if x > XVB_ROUND_DONOR_WHALE_MIN_HR => x - XVB_ROUND_DONOR_WHALE_MIN_HR,
x if x > XVB_ROUND_DONOR_VIP_MIN_HR => x - XVB_ROUND_DONOR_VIP_MIN_HR,
x if x > XVB_ROUND_DONOR_MIN_HR => x - XVB_ROUND_DONOR_MIN_HR,
_ => 0,
}
}
async fn sleep_then_update_node_xmrig(
was_instant: tokio::time::Instant,
spared_time: u32,
client: &Client<HttpConnector>,
api_uri: &str,
token_xmrig: &str,
node: XvbNode,
address: &str,
gui_api: Arc<Mutex<PubXvbApi>>,
) {
info!("Xvb Process | for now mine on p2pol ");
sleep_until(was_instant + Duration::from_secs((XVB_TIME_ALGO - spared_time) as u64)).await;
if let Err(err) =
PrivXmrigApi::update_xmrig_config(client, api_uri, token_xmrig, node, address).await
{
// show to console error about updating xmrig config
if let Err(e) = writeln!(
lock!(gui_api).output,
"Failure to update xmrig config with HTTP API.\nError: {}",
err
) {
error!("XvB Watchdog | GUI status write failed: {}", e);
}
} else {
info!("Xvb Process | mining on XvB pool");
}
}
}
//---------------------------------------------------------------------------------------------------- Public XvB API
use serde_this_or_that::as_u64;
@ -553,6 +724,15 @@ fn signal_interrupt(
//---------------------------------------------------------------------------------------------------- TEST
#[cfg(test)]
mod test {
// use std::{sync::Arc, thread, time::Instant};
// use crate::{
// app::App,
// disk::state::XvbNode,
// helper::{xmrig::PrivXmrigApi, Helper},
// utils::constants::XMRIG_CONFIG_URI,
// };
use std::thread;
use super::XvbPubStats;
@ -571,4 +751,39 @@ mod test {
async fn corr(client: Client<HttpsConnector<hyper::client::HttpConnector>>) -> XvbPubStats {
XvbPubStats::request_api(&client).await.unwrap()
}
// #[test]
// fn update_xmrig_config() {
// let client: hyper::Client<hyper::client::HttpConnector> =
// hyper::Client::builder().build(hyper::client::HttpConnector::new());
// let node = XvbNode::Europe;
// let api_uri = ["http://127.0.0.1:18088/", XMRIG_CONFIG_URI].concat();
// // start app
// let app = App::new(Instant::now());
// // start xmrig
// Helper::start_xmrig(
// &app.helper,
// &app.state.xmrig,
// &app.state.gupax.absolute_xmrig_path,
// Arc::clone(&app.sudo),
// );
// let token = app.state.xmrig.token;
// let address = app.state.p2pool.address;
// // change config
// thread::spawn(move || req_update_config(client, &api_uri, &token, node, &address))
// .join()
// .unwrap();
// }
// #[tokio::main]
// async fn req_update_config(
// client: hyper::Client<hyper::client::HttpConnector>,
// api_uri: &str,
// token: &str,
// node: XvbNode,
// address: &str,
// ) {
// PrivXmrigApi::replace_xmrig_config(client, &api_uri, token, node, address)
// .await
// .unwrap()
// }
}

View file

@ -208,7 +208,7 @@ pub fn init_auto(app: &mut App) {
}
// [Auto-XvB]
if app.state.gupax.auto_xvb {
Helper::start_xvb(&app.helper, &app.state.xvb, &app.state.p2pool);
Helper::start_xvb(&app.helper, &app.state.xvb, &app.state.p2pool, &app.state.xmrig);
} else {
info!("Skipping auto-xvb...");

View file

@ -86,6 +86,7 @@ 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
pub const XMRIG_CONFIG_URI: &str = "1/config"; // The default relative URI of XMRig's API config
// Process state tooltips (online, offline, etc)
pub const P2POOL_ALIVE: &str = "P2Pool is online and fully synchronized";
@ -410,9 +411,15 @@ pub const XMRIG_PATH_EMPTY: &str = "XMRig PATH is empty! To fix: goto the [G
// XvB
pub const XVB_HELP: &str = "You need to register an account by clicking on the link above to get your token with the same p2pool XMR address you use for payment.";
pub const XVB_URL: &str = "https://xmrvsbeast.com";
pub const XVB_URL_PUBLIC_API: &str = "https://xmrvsbeast.com/p2pool/stats";
pub const XVB_URL_RULES: &str = "https://xmrvsbeast.com/p2pool/rules.html";
pub const XVB_URL_PUBLIC_API: &str = "https://xmrvsbeast.com/p2pool/stats";
pub const XVB_NODE_EU: &str = "eu.xmrvsbeast.com:4247";
pub const XVB_NODE_NA: &str = "na.xmrvsbeast.com:4247";
pub const XVB_URL_RULES: &str = "https://xmrvsbeast.com/p2pool/rules.html";
// buffer in percentage of HR to have plus the requirement.
pub const XVB_BUFFER: f32 = 1.05;
// time in second the algorithm will distribute the HR
pub const XVB_TIME_ALGO: u32 = 600;
pub const XVB_TOKEN_LEN: usize = 9;
pub const XVB_HERO_SELECT: &str =
"Donate all spared hashrate to the raffle, even if there is more than enough to be in the most highest round type possible";
@ -422,6 +429,10 @@ pub const XVB_DONATED_1H_FIELD: &str = "Donated last hour";
pub const XVB_DONATED_24H_FIELD: &str = "Donated last 24 hours";
pub const XVB_ROUND_TYPE_FIELD: &str = "Round";
pub const XVB_WINNER_FIELD: &str = "Win";
pub const XVB_ROUND_DONOR_MIN_HR: u32 = 1000;
pub const XVB_ROUND_DONOR_VIP_MIN_HR: u32 = 10000;
pub const XVB_ROUND_DONOR_WHALE_MIN_HR: u32 = 100000;
pub const XVB_ROUND_DONOR_MEGA_MIN_HR: u32 = 1000000;
// CLI argument messages
pub const ARG_HELP: &str = r#"USAGE: ./gupax [--flag]
@ -449,8 +460,13 @@ For more information, see link below:
pub const UNKNOWN_DATA: &str = "???";
// Time PPLNS WINDOW in seconds
// it is an estimation based on number of block in a pplns window and block time (10s). The difficulty of the network should adapt to get close to this value.
pub const TIME_PPLNS_WINDOW_MINI: Duration = Duration::from_secs(2160 * 10);
pub const TIME_PPLNS_WINDOW_MAIN: Duration = Duration::from_secs(363 * 10);
pub const BLOCK_PPLNS_WINDOW_MINI: u64 = 2160;
pub const BLOCK_PPLNS_WINDOW_MAIN: u64 = 363;
pub const SECOND_PER_BLOCK_P2POOL: u64 = 10;
pub const TIME_PPLNS_WINDOW_MINI: Duration =
Duration::from_secs(BLOCK_PPLNS_WINDOW_MINI * SECOND_PER_BLOCK_P2POOL);
pub const TIME_PPLNS_WINDOW_MAIN: Duration =
Duration::from_secs(BLOCK_PPLNS_WINDOW_MAIN * SECOND_PER_BLOCK_P2POOL);
use std::time::Duration;