2022-10-14 21:13:38 +00:00
|
|
|
// 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/>.
|
|
|
|
|
update: save [Version] to state, use runtime [og: State]
[og: State] is now completely wrapped in an [Arc<Mutex>] so that
when the update is done, it can [.lock()] the CURRENT runtime
settings of the user and save to [gupax.toml] instead of using an
old copy that was given to it at the beginning of the thread.
In practice, this means users can change settings around during
an update and the update finishing and saving to disk won't be
using their old settings, but the current ones. Wrapping all of
[og: State] within in [Arc<Mutex>] might be overkill compared to
message channels but [State] really is just a few [bool]'s, [u*],
and small [String]'s, so it's not much data.
To bypass a deadlock when comparing [og == state] every frame,
[og]'s struct fields get cloned every frame into separate
variables, then it gets compared. This is also pretty stupid, but
again, the data being cloned is so tiny that it doesn't seem to
slow anything down.
2022-11-02 17:58:44 +00:00
|
|
|
use serde::{Serialize,Deserialize};
|
2022-10-14 21:13:38 +00:00
|
|
|
use std::time::{Instant,Duration};
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::error::Error;
|
|
|
|
use std::thread;
|
|
|
|
use egui::Color32;
|
2022-10-15 15:24:02 +00:00
|
|
|
use rand::Rng;
|
2022-10-14 21:13:38 +00:00
|
|
|
use log::*;
|
|
|
|
|
|
|
|
// Community Monerod nodes. All of these have ZMQ on 18083.
|
|
|
|
// Adding/removing nodes will need changes to pretty
|
|
|
|
// much all the code in this file, and the code that
|
|
|
|
// handles the actual Enum selector in the P2Pool tab.
|
|
|
|
pub const C3POOL: &'static str = "node.c3pool.com:18081";
|
|
|
|
pub const CAKE: &'static str = "xmr-node.cakewallet.com:18081";
|
|
|
|
pub const CAKE_EU: &'static str = "xmr-node-eu.cakewallet.com:18081";
|
|
|
|
pub const CAKE_UK: &'static str = "xmr-node-uk.cakewallet.com:18081";
|
|
|
|
pub const CAKE_US: &'static str = "xmr-node-usa-east.cakewallet.com:18081";
|
|
|
|
pub const MONERUJO: &'static str = "nodex.monerujo.io:18081";
|
|
|
|
pub const RINO: &'static str = "node.community.rino.io:18081";
|
|
|
|
pub const SELSTA: &'static str = "selsta1.featherwallet.net:18081";
|
|
|
|
pub const SETH: &'static str = "node.sethforprivacy.com:18089";
|
|
|
|
pub const SUPPORTXMR: &'static str = "node.supportxmr.com:18081";
|
|
|
|
pub const SUPPORTXMR_IR: &'static str = "node.supportxmr.ir:18081";
|
|
|
|
pub const XMRVSBEAST: &'static str = "p2pmd.xmrvsbeast.com:18081";
|
|
|
|
|
2022-10-16 21:29:24 +00:00
|
|
|
pub const NODE_IPS: [&'static str; 12] = [
|
|
|
|
C3POOL,CAKE,CAKE_EU,CAKE_UK,CAKE_US,MONERUJO,RINO,
|
|
|
|
SELSTA,SETH,SUPPORTXMR,SUPPORTXMR_IR,XMRVSBEAST,
|
|
|
|
];
|
|
|
|
|
2022-10-14 21:13:38 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct NodeStruct {
|
|
|
|
c3pool: Data, cake: Data, cake_eu: Data, cake_uk: Data, cake_us: Data, monerujo: Data,
|
|
|
|
rino: Data, selsta: Data, seth: Data, supportxmr: Data, supportxmr_ir: Data, xmrvsbeast: Data,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Data {
|
|
|
|
pub ms: u128,
|
|
|
|
pub color: Color32,
|
|
|
|
pub id: NodeEnum,
|
2022-10-16 21:29:24 +00:00
|
|
|
pub ip: &'static str,
|
2022-10-14 21:13:38 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 19:15:27 +00:00
|
|
|
#[derive(Copy,Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]
|
2022-10-14 21:13:38 +00:00
|
|
|
pub enum NodeEnum {
|
|
|
|
C3pool, Cake, CakeEu, CakeUk, CakeUs, Monerujo, Rino,
|
|
|
|
Selsta, Seth, SupportXmr, SupportXmrIr, XmrVsBeast,
|
|
|
|
}
|
|
|
|
|
2022-10-15 19:15:27 +00:00
|
|
|
impl std::fmt::Display for NodeEnum {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
write!(f, "{:#?}", self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-14 21:13:38 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PingResult {
|
2022-10-16 21:29:24 +00:00
|
|
|
pub nodes: NodeStruct,
|
2022-10-14 21:13:38 +00:00
|
|
|
pub fastest: NodeEnum,
|
|
|
|
}
|
|
|
|
|
2022-10-16 21:29:24 +00:00
|
|
|
use crate::NodeEnum::*;
|
2022-10-14 21:13:38 +00:00
|
|
|
impl NodeStruct {
|
|
|
|
pub fn default() -> Self {
|
2022-10-15 15:24:02 +00:00
|
|
|
let ms = 0;
|
|
|
|
let color = Color32::GRAY;
|
2022-10-14 21:13:38 +00:00
|
|
|
Self {
|
2022-10-16 21:29:24 +00:00
|
|
|
c3pool: Data { ms, color, id: C3pool, ip: C3POOL, },
|
|
|
|
cake: Data { ms, color, id: Cake, ip: CAKE, },
|
|
|
|
cake_eu: Data { ms, color, id: CakeEu, ip: CAKE_EU, },
|
|
|
|
cake_uk: Data { ms, color, id: CakeUk, ip: CAKE_UK, },
|
|
|
|
cake_us: Data { ms, color, id: CakeUs, ip: CAKE_US, },
|
|
|
|
monerujo: Data { ms, color, id: Monerujo, ip: MONERUJO, },
|
|
|
|
rino: Data { ms, color, id: Rino, ip: RINO, },
|
|
|
|
selsta: Data { ms, color, id: Selsta, ip: SELSTA, },
|
|
|
|
seth: Data { ms, color, id: Seth, ip: SETH, },
|
|
|
|
supportxmr: Data { ms, color, id: SupportXmr, ip: SUPPORTXMR, },
|
|
|
|
supportxmr_ir: Data { ms, color, id: SupportXmrIr, ip: SUPPORTXMR_IR, },
|
|
|
|
xmrvsbeast: Data { ms, color, id: XmrVsBeast, ip: XMRVSBEAST, },
|
2022-10-14 21:13:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is for pinging the community nodes to
|
|
|
|
// find the fastest/slowest one for the user.
|
|
|
|
// The process:
|
2022-10-15 15:24:02 +00:00
|
|
|
// - Send [get_info] JSON-RPC requests over HTTP
|
2022-10-25 02:58:42 +00:00
|
|
|
// - To prevent fingerprinting, randomly send [2-4] calls
|
2022-10-14 21:13:38 +00:00
|
|
|
// - Measure each request in milliseconds as [u128]
|
|
|
|
// - Timeout on requests over 5 seconds
|
|
|
|
// - Calculate average time
|
|
|
|
// - Add data to appropriate struct
|
|
|
|
// - Sort fastest to lowest
|
|
|
|
// - Return [PingResult(NodeStruct, NodeEnum)] (data and fastest node)
|
2022-10-18 19:26:21 +00:00
|
|
|
//
|
2022-10-14 21:13:38 +00:00
|
|
|
// This is done linearly since per IP since
|
|
|
|
// multi-threading might affect performance.
|
|
|
|
//
|
|
|
|
// <300ms = GREEN
|
|
|
|
// <1000ms = YELLOW
|
|
|
|
// >1000ms = RED
|
|
|
|
// timeout = BLACK
|
|
|
|
// default = GRAY
|
|
|
|
pub fn ping() -> PingResult {
|
|
|
|
info!("Starting community node pings...");
|
2022-10-25 02:58:42 +00:00
|
|
|
// Get node list
|
2022-10-16 21:29:24 +00:00
|
|
|
let mut nodes = NodeStruct::default();
|
2022-10-25 02:58:42 +00:00
|
|
|
|
|
|
|
// Create JSON request
|
2022-10-14 21:13:38 +00:00
|
|
|
let mut get_info = HashMap::new();
|
|
|
|
get_info.insert("jsonrpc", "2.0");
|
|
|
|
get_info.insert("id", "0");
|
|
|
|
get_info.insert("method", "get_info");
|
2022-10-25 02:58:42 +00:00
|
|
|
|
|
|
|
// Misc Settings
|
2022-10-14 21:13:38 +00:00
|
|
|
let mut vec: Vec<(u128, NodeEnum)> = Vec::new();
|
2022-10-25 02:58:42 +00:00
|
|
|
|
|
|
|
// Create HTTP Client
|
2022-10-14 21:13:38 +00:00
|
|
|
let timeout_sec = Duration::from_millis(5000);
|
2022-10-25 02:58:42 +00:00
|
|
|
let client = reqwest::blocking::ClientBuilder::new();
|
|
|
|
let client = reqwest::blocking::ClientBuilder::timeout(client, timeout_sec);
|
|
|
|
let client = reqwest::blocking::ClientBuilder::build(client).unwrap();
|
2022-10-14 21:13:38 +00:00
|
|
|
|
2022-10-16 21:29:24 +00:00
|
|
|
for ip in NODE_IPS.iter() {
|
2022-10-25 02:58:42 +00:00
|
|
|
// Match IP
|
2022-10-14 21:13:38 +00:00
|
|
|
let id = match *ip {
|
|
|
|
C3POOL => C3pool,
|
|
|
|
CAKE => Cake,
|
|
|
|
CAKE_EU => CakeEu,
|
|
|
|
CAKE_UK => CakeUk,
|
|
|
|
CAKE_US => CakeUs,
|
|
|
|
MONERUJO => Monerujo,
|
|
|
|
RINO => Rino,
|
|
|
|
SELSTA => Selsta,
|
|
|
|
SETH => Seth,
|
|
|
|
SUPPORTXMR => SupportXmr,
|
|
|
|
SUPPORTXMR_IR => SupportXmrIr,
|
|
|
|
_ => XmrVsBeast,
|
|
|
|
};
|
2022-10-25 02:58:42 +00:00
|
|
|
// Misc
|
2022-10-14 21:13:38 +00:00
|
|
|
let mut timeout = false;
|
|
|
|
let mut mid = Duration::new(0, 0);
|
2022-10-25 02:58:42 +00:00
|
|
|
let max = rand::thread_rng().gen_range(2..4);
|
|
|
|
|
|
|
|
// Start JSON-RPC request
|
2022-10-14 21:13:38 +00:00
|
|
|
for i in 1..=max {
|
|
|
|
let now = Instant::now();
|
2022-10-25 02:58:42 +00:00
|
|
|
let http = "http://".to_owned() + &**ip + "/json_rpc";
|
2022-10-14 21:13:38 +00:00
|
|
|
match client.post(http).json(&get_info).send() {
|
2022-10-25 02:58:42 +00:00
|
|
|
Ok(_) => mid += now.elapsed(),
|
2022-10-14 21:13:38 +00:00
|
|
|
Err(err) => {
|
2022-10-25 02:58:42 +00:00
|
|
|
error!("Timeout on [{:#?}: {}] (over 5 seconds) | {}", id, ip, err);
|
2022-10-14 21:13:38 +00:00
|
|
|
mid += timeout_sec;
|
|
|
|
timeout = true;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
2022-10-25 02:58:42 +00:00
|
|
|
|
|
|
|
// Calculate average
|
2022-10-14 21:13:38 +00:00
|
|
|
let ms = mid.as_millis() / 3;
|
|
|
|
vec.push((ms, id));
|
2022-10-15 15:24:02 +00:00
|
|
|
info!("{}ms ... {} calls ... {}", ms, max, ip);
|
2022-10-14 21:13:38 +00:00
|
|
|
let color: Color32;
|
|
|
|
if timeout == true {
|
|
|
|
color = Color32::BLACK
|
|
|
|
} else if ms >= 1000 {
|
|
|
|
color = Color32::LIGHT_RED
|
|
|
|
} else if ms >= 300 {
|
|
|
|
color = Color32::LIGHT_YELLOW
|
|
|
|
} else {
|
|
|
|
color = Color32::LIGHT_GREEN
|
|
|
|
}
|
|
|
|
match id {
|
2022-10-16 21:29:24 +00:00
|
|
|
C3pool => { nodes.c3pool.ms = ms; nodes.c3pool.color = color; },
|
|
|
|
Cake => { nodes.cake.ms = ms; nodes.cake.color = color; },
|
|
|
|
CakeEu => { nodes.cake_eu.ms = ms; nodes.cake_eu.color = color; },
|
|
|
|
CakeUk => { nodes.cake_uk.ms = ms; nodes.cake_uk.color = color; },
|
|
|
|
CakeUs => { nodes.cake_us.ms = ms; nodes.cake_us.color = color; },
|
|
|
|
Monerujo => { nodes.monerujo.ms = ms; nodes.monerujo.color = color; },
|
|
|
|
Rino => { nodes.rino.ms = ms; nodes.rino.color = color; },
|
|
|
|
Selsta => { nodes.selsta.ms = ms; nodes.selsta.color = color; },
|
|
|
|
Seth => { nodes.seth.ms = ms; nodes.seth.color = color; },
|
|
|
|
SupportXmr => { nodes.supportxmr.ms = ms; nodes.supportxmr.color = color; },
|
|
|
|
SupportXmrIr => { nodes.supportxmr_ir.ms = ms; nodes.supportxmr_ir.color = color; },
|
|
|
|
XmrVsBeast => { nodes.xmrvsbeast.ms = ms; nodes.xmrvsbeast.color = color; },
|
2022-10-14 21:13:38 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-25 02:58:42 +00:00
|
|
|
|
|
|
|
// Calculate fastest out of all nodes
|
2022-10-14 21:13:38 +00:00
|
|
|
let mut best_ms: u128 = vec[0].0;
|
|
|
|
let mut fastest: NodeEnum = vec[0].1;
|
|
|
|
for (ms, id) in vec.iter() {
|
|
|
|
if ms < &best_ms {
|
|
|
|
fastest = *id;
|
|
|
|
best_ms = *ms;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// These values have weird behavior.
|
|
|
|
// The values don't update if not printed beforehand,
|
|
|
|
// so the match below on [fastest] gets funky.
|
|
|
|
info!("Fastest node ... {:#?} @ {:#?}ms", fastest, best_ms);
|
|
|
|
info!("Community node ping ... OK");
|
2022-10-16 21:29:24 +00:00
|
|
|
PingResult { nodes, fastest, }
|
2022-10-14 21:13:38 +00:00
|
|
|
}
|
|
|
|
}
|