mirror of
https://github.com/Cyrix126/gupaxx.git
synced 2024-12-22 22:59:27 +00:00
update: implement tor/arti, tls, basic metadata fetch
This commit is contained in:
parent
5a686aaa1d
commit
bf81b2c57c
8 changed files with 2611 additions and 109 deletions
2153
Cargo.lock
generated
2153
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
18
Cargo.toml
18
Cargo.toml
|
@ -4,27 +4,41 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dirs = "4.0.0"
|
anyhow = "1.0.65"
|
||||||
|
arti-client = "0.7.0"
|
||||||
|
arti-hyper = "0.7.0"
|
||||||
|
bytes = "1.2.1"
|
||||||
chrono = "0.4.22"
|
chrono = "0.4.22"
|
||||||
|
dirs = "4.0.0"
|
||||||
eframe = "0.19.0"
|
eframe = "0.19.0"
|
||||||
egui = "0.19.0"
|
egui = "0.19.0"
|
||||||
egui_extras = { version = "0.19.0", features = ["image"] }
|
egui_extras = { version = "0.19.0", features = ["image"] }
|
||||||
env_logger = "0.9.1"
|
env_logger = "0.9.1"
|
||||||
figment = { version = "0.10.8", features = ["toml"] }
|
figment = { version = "0.10.8", features = ["toml"] }
|
||||||
|
flate2 = "1.0"
|
||||||
hex-literal = "0.3.4"
|
hex-literal = "0.3.4"
|
||||||
|
hyper = "0.14.20"
|
||||||
|
hyper-tls = "0.5.0"
|
||||||
image = { version = "0.24.4", features = ["png"] }
|
image = { version = "0.24.4", features = ["png"] }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
monero = "0.18.0"
|
monero = "0.18.0"
|
||||||
num_cpus = "1.13.1"
|
num_cpus = "1.13.1"
|
||||||
num-format = "0.4.0"
|
num-format = "0.4.0"
|
||||||
|
openssl = { version = "*", features = ["vendored"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
regex = "1.6.0"
|
regex = "1.6.0"
|
||||||
reqwest = { version = "0.11.12", features = ["blocking", "json"] }
|
reqwest = { version = "0.11.12", features = ["blocking", "json"] }
|
||||||
serde = "1.0.145"
|
serde = "1.0.145"
|
||||||
serde_derive = "1.0.145"
|
serde_derive = "1.0.145"
|
||||||
|
serde_json = "1.0"
|
||||||
sha2 = "0.10.6"
|
sha2 = "0.10.6"
|
||||||
|
tar = "0.4.38"
|
||||||
|
tls-api = "0.9.0"
|
||||||
|
tls-api-native-tls = "0.9.0"
|
||||||
|
tokio = { version = "1.21.2", features = ["full"] }
|
||||||
toml = "0.5.9"
|
toml = "0.5.9"
|
||||||
openssl = { version = "*", features = ["vendored"] }
|
tor-rtcompat = "0.7.0"
|
||||||
|
walkdir = "2.3.2"
|
||||||
|
|
||||||
[profile.optimized]
|
[profile.optimized]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
|
@ -26,11 +26,6 @@ pub const P2POOL_BASE_ARGS: &'static str = "";
|
||||||
pub const XMRIG_BASE_ARGS: &'static str = "--http-host=127.0.0.1 --http-port=18088 --algo=rx/0 --coin=Monero --randomx-cache-qos";
|
pub const XMRIG_BASE_ARGS: &'static str = "--http-host=127.0.0.1 --http-port=18088 --algo=rx/0 --coin=Monero --randomx-cache-qos";
|
||||||
pub const HORIZONTAL: &'static str = "--------------------------------------------";
|
pub const HORIZONTAL: &'static str = "--------------------------------------------";
|
||||||
|
|
||||||
// Update data
|
|
||||||
pub const GITHUB_METADATA_GUPAX: &'static str = "https://api.github.com/repos/hinto-janaiyo/gupax/releases/latest";
|
|
||||||
pub const GITHUB_METADATA_P2POOL: &'static str = "https://api.github.com/repos/SChernykh/p2pool/releases/latest";
|
|
||||||
pub const GITHUB_METADATA_XMRIG: &'static str = "https://api.github.com/repos/xmrig/xmrig/releases/latest";
|
|
||||||
|
|
||||||
// OS specific
|
// OS specific
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub const OS: &'static str = " Windows";
|
pub const OS: &'static str = " Windows";
|
||||||
|
|
|
@ -20,6 +20,7 @@ use crate::App;
|
||||||
use egui::WidgetType::Button;
|
use egui::WidgetType::Button;
|
||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
use crate::state::Gupax;
|
use crate::state::Gupax;
|
||||||
|
use crate::update::*;
|
||||||
|
|
||||||
impl Gupax {
|
impl Gupax {
|
||||||
pub fn show(state: &mut Gupax, ctx: &egui::Context, ui: &mut egui::Ui) {
|
pub fn show(state: &mut Gupax, ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||||
|
|
41
src/main.rs
41
src/main.rs
|
@ -50,7 +50,8 @@ mod status;
|
||||||
mod gupax;
|
mod gupax;
|
||||||
mod p2pool;
|
mod p2pool;
|
||||||
mod xmrig;
|
mod xmrig;
|
||||||
use {ferris::*,constants::*,node::*,state::*,about::*,status::*,gupax::*,p2pool::*,xmrig::*};
|
mod update;
|
||||||
|
use {ferris::*,constants::*,node::*,state::*,about::*,status::*,gupax::*,p2pool::*,xmrig::*,update::*};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Struct + Impl
|
//---------------------------------------------------------------------------------------------------- Struct + Impl
|
||||||
// The state of the outer main [App].
|
// The state of the outer main [App].
|
||||||
|
@ -66,15 +67,11 @@ pub struct App {
|
||||||
node: Arc<Mutex<NodeStruct>>, // Data on community nodes
|
node: Arc<Mutex<NodeStruct>>, // Data on community nodes
|
||||||
width: f32, // Top-level width
|
width: f32, // Top-level width
|
||||||
height: f32, // Top-level height
|
height: f32, // Top-level height
|
||||||
// State:
|
// State
|
||||||
// og = Old state to compare against
|
og: State, // og = Old state to compare against
|
||||||
// state = Working state (current settings)
|
state: State, // state = Working state (current settings)
|
||||||
// Instead of comparing [og == state] every frame,
|
update: Update, // State for update data [update.rs]
|
||||||
// the [diff] bool will be the signal for [Reset/Save].
|
diff: bool, // Instead of comparing [og == state] every frame, this bool indicates changes
|
||||||
og: State,
|
|
||||||
state: State,
|
|
||||||
// update: Update, // State for update data [update.rs]
|
|
||||||
diff: bool,
|
|
||||||
// Process/update state:
|
// Process/update state:
|
||||||
// Doesn't make sense to save this on disk
|
// Doesn't make sense to save this on disk
|
||||||
// so it's represented as a bool here.
|
// so it's represented as a bool here.
|
||||||
|
@ -105,6 +102,7 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
|
let throwaway = State::default();
|
||||||
let app = Self {
|
let app = Self {
|
||||||
tab: Tab::default(),
|
tab: Tab::default(),
|
||||||
quit: false,
|
quit: false,
|
||||||
|
@ -116,7 +114,7 @@ impl App {
|
||||||
node: Arc::new(Mutex::new(NodeStruct::default())),
|
node: Arc::new(Mutex::new(NodeStruct::default())),
|
||||||
og: State::default(),
|
og: State::default(),
|
||||||
state: State::default(),
|
state: State::default(),
|
||||||
// update: Update::default(),
|
update: Update::new(&throwaway.gupax.absolute_p2pool_path, &throwaway.gupax.absolute_xmrig_path, true),
|
||||||
diff: false,
|
diff: false,
|
||||||
p2pool: false,
|
p2pool: false,
|
||||||
xmrig: false,
|
xmrig: false,
|
||||||
|
@ -149,6 +147,7 @@ impl App {
|
||||||
app.og.xmrig.max_threads = num_cpus::get();
|
app.og.xmrig.max_threads = num_cpus::get();
|
||||||
if app.og.xmrig.current_threads > app.og.xmrig.max_threads { app.og.xmrig.current_threads = app.og.xmrig.max_threads; }
|
if app.og.xmrig.current_threads > app.og.xmrig.max_threads { app.og.xmrig.current_threads = app.og.xmrig.max_threads; }
|
||||||
app.state = app.og.clone();
|
app.state = app.og.clone();
|
||||||
|
app.update = Update::new(&app.og.gupax.absolute_p2pool_path, &app.og.gupax.absolute_xmrig_path, app.og.gupax.update_via_tor);
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,14 +272,23 @@ fn parse_args(mut app: App) -> App {
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_exe_dir() -> Result<String, std::io::Error> {
|
// Get absolute [Gupax] binary path
|
||||||
|
pub fn get_exe() -> Result<String, std::io::Error> {
|
||||||
match std::env::current_exe() {
|
match std::env::current_exe() {
|
||||||
Ok(mut path) => { path.pop(); Ok(path.into_os_string().into_string().unwrap()) },
|
Ok(mut path) => { Ok(path.display().to_string()) },
|
||||||
Err(err) => { error!("Couldn't get exe basepath PATH"); return Err(err) },
|
Err(err) => { error!("Couldn't get exe basepath PATH"); return Err(err) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rand_tmp(path: &String) -> String {
|
// Get absolute [Gupax] directory path
|
||||||
|
pub fn get_exe_dir() -> Result<String, std::io::Error> {
|
||||||
|
match std::env::current_exe() {
|
||||||
|
Ok(mut path) => { path.pop(); Ok(path.display().to_string()) },
|
||||||
|
Err(err) => { error!("Couldn't get exe basepath PATH"); return Err(err) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_rand_tmp(path: &String) -> String {
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use rand::distributions::Alphanumeric;
|
use rand::distributions::Alphanumeric;
|
||||||
let rand: String = thread_rng()
|
let rand: String = thread_rng()
|
||||||
|
@ -361,6 +369,11 @@ impl eframe::App for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||||
|
// *-------*
|
||||||
|
// | DEBUG |
|
||||||
|
// *-------*
|
||||||
|
self.update.start();
|
||||||
|
thread::sleep;
|
||||||
// This sets the top level Ui dimensions.
|
// This sets the top level Ui dimensions.
|
||||||
// Used as a reference for other uis.
|
// Used as a reference for other uis.
|
||||||
egui::CentralPanel::default().show(ctx, |ui| { self.width = ui.available_width(); self.height = ui.available_height(); });
|
egui::CentralPanel::default().show(ctx, |ui| { self.width = ui.available_width(); self.height = ui.available_height(); });
|
||||||
|
|
33
src/node.rs
33
src/node.rs
|
@ -103,7 +103,7 @@ impl NodeStruct {
|
||||||
// find the fastest/slowest one for the user.
|
// find the fastest/slowest one for the user.
|
||||||
// The process:
|
// The process:
|
||||||
// - Send [get_info] JSON-RPC requests over HTTP
|
// - Send [get_info] JSON-RPC requests over HTTP
|
||||||
// - To prevent fingerprinting, randomly send [1-5] calls
|
// - To prevent fingerprinting, randomly send [2-4] calls
|
||||||
// - Measure each request in milliseconds as [u128]
|
// - Measure each request in milliseconds as [u128]
|
||||||
// - Timeout on requests over 5 seconds
|
// - Timeout on requests over 5 seconds
|
||||||
// - Calculate average time
|
// - Calculate average time
|
||||||
|
@ -121,16 +121,26 @@ impl NodeStruct {
|
||||||
// default = GRAY
|
// default = GRAY
|
||||||
pub fn ping() -> PingResult {
|
pub fn ping() -> PingResult {
|
||||||
info!("Starting community node pings...");
|
info!("Starting community node pings...");
|
||||||
|
// Get node list
|
||||||
let mut nodes = NodeStruct::default();
|
let mut nodes = NodeStruct::default();
|
||||||
|
|
||||||
|
// Create JSON request
|
||||||
let mut get_info = HashMap::new();
|
let mut get_info = HashMap::new();
|
||||||
get_info.insert("jsonrpc", "2.0");
|
get_info.insert("jsonrpc", "2.0");
|
||||||
get_info.insert("id", "0");
|
get_info.insert("id", "0");
|
||||||
get_info.insert("method", "get_info");
|
get_info.insert("method", "get_info");
|
||||||
|
|
||||||
|
// Misc Settings
|
||||||
let mut vec: Vec<(u128, NodeEnum)> = Vec::new();
|
let mut vec: Vec<(u128, NodeEnum)> = Vec::new();
|
||||||
let fastest = false;
|
|
||||||
|
// Create HTTP Client
|
||||||
let timeout_sec = Duration::from_millis(5000);
|
let timeout_sec = Duration::from_millis(5000);
|
||||||
|
let client = reqwest::blocking::ClientBuilder::new();
|
||||||
|
let client = reqwest::blocking::ClientBuilder::timeout(client, timeout_sec);
|
||||||
|
let client = reqwest::blocking::ClientBuilder::build(client).unwrap();
|
||||||
|
|
||||||
for ip in NODE_IPS.iter() {
|
for ip in NODE_IPS.iter() {
|
||||||
|
// Match IP
|
||||||
let id = match *ip {
|
let id = match *ip {
|
||||||
C3POOL => C3pool,
|
C3POOL => C3pool,
|
||||||
CAKE => Cake,
|
CAKE => Cake,
|
||||||
|
@ -143,27 +153,28 @@ impl NodeStruct {
|
||||||
SETH => Seth,
|
SETH => Seth,
|
||||||
SUPPORTXMR => SupportXmr,
|
SUPPORTXMR => SupportXmr,
|
||||||
SUPPORTXMR_IR => SupportXmrIr,
|
SUPPORTXMR_IR => SupportXmrIr,
|
||||||
// XMRVSBEAST => XmrVsBeast,
|
|
||||||
_ => XmrVsBeast,
|
_ => XmrVsBeast,
|
||||||
};
|
};
|
||||||
|
// Misc
|
||||||
let mut timeout = false;
|
let mut timeout = false;
|
||||||
let mut mid = Duration::new(0, 0);
|
let mut mid = Duration::new(0, 0);
|
||||||
let max = rand::thread_rng().gen_range(1..5);
|
let max = rand::thread_rng().gen_range(2..4);
|
||||||
|
|
||||||
|
// Start JSON-RPC request
|
||||||
for i in 1..=max {
|
for i in 1..=max {
|
||||||
let client = reqwest::blocking::ClientBuilder::new();
|
|
||||||
let client = reqwest::blocking::ClientBuilder::timeout(client, timeout_sec);
|
|
||||||
let client = reqwest::blocking::ClientBuilder::build(client).unwrap();
|
|
||||||
let http = "http://".to_owned() + &**ip + "/json_rpc";
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
|
let http = "http://".to_owned() + &**ip + "/json_rpc";
|
||||||
match client.post(http).json(&get_info).send() {
|
match client.post(http).json(&get_info).send() {
|
||||||
Ok(r) => mid += now.elapsed(),
|
Ok(_) => mid += now.elapsed(),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Timeout on [{:#?}: {}] (over 5 seconds)", id, ip);
|
error!("Timeout on [{:#?}: {}] (over 5 seconds) | {}", id, ip, err);
|
||||||
mid += timeout_sec;
|
mid += timeout_sec;
|
||||||
timeout = true;
|
timeout = true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate average
|
||||||
let ms = mid.as_millis() / 3;
|
let ms = mid.as_millis() / 3;
|
||||||
vec.push((ms, id));
|
vec.push((ms, id));
|
||||||
info!("{}ms ... {} calls ... {}", ms, max, ip);
|
info!("{}ms ... {} calls ... {}", ms, max, ip);
|
||||||
|
@ -192,6 +203,8 @@ impl NodeStruct {
|
||||||
XmrVsBeast => { nodes.xmrvsbeast.ms = ms; nodes.xmrvsbeast.color = color; },
|
XmrVsBeast => { nodes.xmrvsbeast.ms = ms; nodes.xmrvsbeast.color = color; },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate fastest out of all nodes
|
||||||
let mut best_ms: u128 = vec[0].0;
|
let mut best_ms: u128 = vec[0].0;
|
||||||
let mut fastest: NodeEnum = vec[0].1;
|
let mut fastest: NodeEnum = vec[0].1;
|
||||||
for (ms, id) in vec.iter() {
|
for (ms, id) in vec.iter() {
|
||||||
|
|
|
@ -51,6 +51,7 @@ impl State {
|
||||||
auto_node: true,
|
auto_node: true,
|
||||||
ask_before_quit: true,
|
ask_before_quit: true,
|
||||||
save_before_quit: true,
|
save_before_quit: true,
|
||||||
|
update_via_tor: true,
|
||||||
p2pool_path: DEFAULT_P2POOL_PATH.to_string(),
|
p2pool_path: DEFAULT_P2POOL_PATH.to_string(),
|
||||||
xmrig_path: DEFAULT_XMRIG_PATH.to_string(),
|
xmrig_path: DEFAULT_XMRIG_PATH.to_string(),
|
||||||
absolute_p2pool_path: Self::into_absolute_path(DEFAULT_P2POOL_PATH.to_string()).unwrap(),
|
absolute_p2pool_path: Self::into_absolute_path(DEFAULT_P2POOL_PATH.to_string()).unwrap(),
|
||||||
|
@ -300,6 +301,7 @@ pub struct Gupax {
|
||||||
pub auto_node: bool,
|
pub auto_node: bool,
|
||||||
pub ask_before_quit: bool,
|
pub ask_before_quit: bool,
|
||||||
pub save_before_quit: bool,
|
pub save_before_quit: bool,
|
||||||
|
pub update_via_tor: bool,
|
||||||
pub p2pool_path: String,
|
pub p2pool_path: String,
|
||||||
pub xmrig_path: String,
|
pub xmrig_path: String,
|
||||||
pub absolute_p2pool_path: PathBuf,
|
pub absolute_p2pool_path: PathBuf,
|
||||||
|
|
465
src/update.rs
465
src/update.rs
|
@ -14,54 +14,455 @@
|
||||||
//// You should have received a copy of the GNU General Public License
|
//// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::State;
|
//---------------------------------------------------------------------------------------------------- Imports
|
||||||
|
use serde_derive::{Serialize,Deserialize};
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::sync::{Arc,Mutex};
|
||||||
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
|
use std::io::{Read,Write};
|
||||||
|
//use crate::{Name::*,State};
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
use rand::distributions::Alphanumeric;
|
||||||
|
use anyhow::Error;
|
||||||
|
use arti_hyper::*;
|
||||||
|
use arti_client::{TorClient,TorClientConfig};
|
||||||
|
use tokio::io::{AsyncReadExt,AsyncWriteExt};
|
||||||
|
use tls_api::{TlsConnector, TlsConnectorBuilder};
|
||||||
|
use hyper::header::HeaderValue;
|
||||||
|
use hyper::{Client,Body,Request};
|
||||||
|
use hyper_tls::HttpsConnector;
|
||||||
|
use arti_hyper::*;
|
||||||
|
use log::*;
|
||||||
|
use crate::update::Name::*;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
struct Update {
|
// use tls_api_native_tls::{TlsConnector,TlsConnectorBuilder};
|
||||||
new_gupax: String,
|
|
||||||
new_p2pool: String,
|
//---------------------------------------------------------------------------------------------------- Constants
|
||||||
new_xmrig: String,
|
// Package naming schemes:
|
||||||
path_gupax: String,
|
// gupax | gupax-vX.X.X-(windows|macos|linux)-x64.(zip|tar.gz)
|
||||||
path_p2pool: String,
|
// p2pool | p2pool-vX.X.X-(windows|macos|linux)-x64.(zip|tar.gz)
|
||||||
path_xmrig: String,
|
// xmrig | xmrig-X.X.X-(msvc-win64|macos-x64|linux-static-x64).(zip|tar.gz)
|
||||||
updating: Arc<Mutex<bool>> // Is the update in progress?
|
//
|
||||||
update_prog: u8, // Not an [f32] because [Eq] doesn't work
|
// Download link = PREFIX + Version (found at runtime) + SUFFIX + Version + EXT
|
||||||
|
// Example: https://github.com/hinto-janaiyo/gupax/releases/download/v0.0.1/gupax-v0.0.1-linux-standalone-x64
|
||||||
|
//
|
||||||
|
// Exceptions (there are always exceptions...):
|
||||||
|
// - XMRig doesn't have a [v], so it is [xmrig-6.18.0-...]
|
||||||
|
// - XMRig separates the hash and signature
|
||||||
|
// - P2Pool hashes are in UPPERCASE
|
||||||
|
// - Gupax will be downloaded as a standalone binary (no decompression/extraction needed)
|
||||||
|
|
||||||
|
const GUPAX_METADATA: &'static str = "https://api.github.com/repos/hinto-janaiyo/gupax/releases/latest";
|
||||||
|
const P2POOL_METADATA: &'static str = "https://api.github.com/repos/SChernykh/p2pool/releases/latest";
|
||||||
|
const XMRIG_METADATA: &'static str = "https://api.github.com/repos/xmrig/xmrig/releases/latest";
|
||||||
|
|
||||||
|
const GUPAX_PREFIX: &'static str = "https://github.com/hinto-janaiyo/gupax/releases/download/";
|
||||||
|
const P2POOL_PREFIX: &'static str = "https://github.com/SChernykh/p2pool/releases/download/";
|
||||||
|
const XMRIG_PREFIX: &'static str = "https://github.com/xmrig/xmrig/releases/download/";
|
||||||
|
|
||||||
|
const GUPAX_SUFFIX: &'static str = "/gupax-";
|
||||||
|
const P2POOL_SUFFIX: &'static str = "/p2pool-";
|
||||||
|
const XMRIG_SUFFIX: &'static str = "/xmrig-";
|
||||||
|
|
||||||
|
const GUPAX_HASH: &'static str = "SHA256SUMS";
|
||||||
|
const P2POOL_HASH: &'static str = "sha256sums.txt.asc";
|
||||||
|
const XMRIG_HASH: &'static str = "SHA256SUMS";
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
const GUPAX_EXTENSION: &'static str = "-windows-standalone-x64.exe";
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
const P2POOL_EXTENSION: &'static str = "-windows-x64.zip";
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
const XMRIG_EXTENSION: &'static str = "-msvc-win64.zip";
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
const GUPAX_EXTENSION: &'static str = "-macos-standalone-x64";
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
const P2POOL_EXTENSION: &'static str = "-macos-x64.tar.gz";
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
const XMRIG_EXTENSION: &'static str = "-macos-x64.tar.gz";
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
const GUPAX_EXTENSION: &'static str = "-linux-standalone-x64";
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
const P2POOL_EXTENSION: &'static str = "-linux-x64.tar.gz";
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
const XMRIG_EXTENSION: &'static str = "-linux-static-x64.tar.gz";
|
||||||
|
|
||||||
|
// Some fake Curl/Wget user-agents because GitHub API requires one and a Tor browser
|
||||||
|
// user-agent might be fingerprintable without all the associated headers.
|
||||||
|
const FAKE_USER_AGENT: [&'static str; 50] = [
|
||||||
|
"Wget/1.16.3",
|
||||||
|
"Wget/1.17",
|
||||||
|
"Wget/1.17.1",
|
||||||
|
"Wget/1.18",
|
||||||
|
"Wget/1.18",
|
||||||
|
"Wget/1.19",
|
||||||
|
"Wget/1.19.1",
|
||||||
|
"Wget/1.19.2",
|
||||||
|
"Wget/1.19.3",
|
||||||
|
"Wget/1.19.4",
|
||||||
|
"Wget/1.19.5",
|
||||||
|
"Wget/1.20",
|
||||||
|
"Wget/1.20.1",
|
||||||
|
"Wget/1.20.2",
|
||||||
|
"Wget/1.20.3",
|
||||||
|
"Wget/1.21",
|
||||||
|
"Wget/1.21.1",
|
||||||
|
"Wget/1.21.2",
|
||||||
|
"Wget/1.21.3",
|
||||||
|
"curl/7.64.1",
|
||||||
|
"curl/7.65.0",
|
||||||
|
"curl/7.65.1",
|
||||||
|
"curl/7.65.2",
|
||||||
|
"curl/7.65.3",
|
||||||
|
"curl/7.66.0",
|
||||||
|
"curl/7.67.0",
|
||||||
|
"curl/7.68.0",
|
||||||
|
"curl/7.69.0",
|
||||||
|
"curl/7.69.1",
|
||||||
|
"curl/7.70.0",
|
||||||
|
"curl/7.70.1",
|
||||||
|
"curl/7.71.0",
|
||||||
|
"curl/7.71.1",
|
||||||
|
"curl/7.72.0",
|
||||||
|
"curl/7.73.0",
|
||||||
|
"curl/7.74.0",
|
||||||
|
"curl/7.75.0",
|
||||||
|
"curl/7.76.0",
|
||||||
|
"curl/7.76.1",
|
||||||
|
"curl/7.77.0",
|
||||||
|
"curl/7.78.0",
|
||||||
|
"curl/7.79.0",
|
||||||
|
"curl/7.79.1",
|
||||||
|
"curl/7.80.0",
|
||||||
|
"curl/7.81.0",
|
||||||
|
"curl/7.82.0",
|
||||||
|
"curl/7.83.0",
|
||||||
|
"curl/7.83.1",
|
||||||
|
"curl/7.84.0",
|
||||||
|
"curl/7.85.0",
|
||||||
|
];
|
||||||
|
|
||||||
|
const MSG_START: &'static str = "Starting update...";
|
||||||
|
const MSG_TMP: &'static str = "Creating temporary directory";
|
||||||
|
const MSG_PKG: &'static str = "Creating package list";
|
||||||
|
const MSG_TOR: &'static str = "Creating Tor+HTTPS client";
|
||||||
|
const MSG_HTTPS: &'static str = "Creating HTTPS client";
|
||||||
|
const MSG_METADATA: &'static str = "Fetching package metadata";
|
||||||
|
const MSG_ARCHIVE: &'static str = "Downloading packages";
|
||||||
|
|
||||||
|
// These two are sequential and not async so no need for a constant message.
|
||||||
|
// The package in question will be known at runtime, so that will be printed.
|
||||||
|
//const MSG_EXTRACT: &'static str = "Extracting packages";
|
||||||
|
//const MSG_UPGRADE: &'static str = "Upgrading packages";
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Update struct/impl
|
||||||
|
// Contains values needed during update
|
||||||
|
// Progress bar structure:
|
||||||
|
// 5% | Create tmp directory
|
||||||
|
// 5% | Create package list
|
||||||
|
// 10% | Create Tor/HTTPS client
|
||||||
|
// 20% | Download Metadata (x3)
|
||||||
|
// 30% | Download Archive (x3)
|
||||||
|
// 15% | Extract (x3)
|
||||||
|
// 15% | Upgrade (x3)
|
||||||
|
|
||||||
|
pub struct Update {
|
||||||
|
path_gupax: String, // Full path to current gupax
|
||||||
|
path_p2pool: String, // Full path to current p2pool
|
||||||
|
path_xmrig: String, // Full path to current xmrig
|
||||||
|
tmp_dir: String, // Full path to temporary directory
|
||||||
|
updating: Arc<Mutex<bool>>, // Is an update in progress?
|
||||||
|
prog: Arc<Mutex<u8>>, // Holds the 0-100% progress bar number
|
||||||
|
msg: Arc<Mutex<String>>, // Message to display on [Gupax] tab while updating
|
||||||
|
tor: bool, // Is Tor enabled or not?
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Update {
|
impl Update {
|
||||||
fn new(path_p2pool: String, path_xmrig: String) -> Result<Self, Error> {
|
// Takes in current paths from [State]
|
||||||
let path_gupax = std::env::current_exe()?;
|
pub fn new(path_p2pool: &PathBuf, path_xmrig: &PathBuf, tor: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
new_gupax: "?".to_string(),
|
path_gupax: crate::get_exe().unwrap(),
|
||||||
new_p2pool: "?".to_string(),
|
path_p2pool: path_p2pool.display().to_string(),
|
||||||
new_xmrig: "?".to_string(),
|
path_xmrig: path_xmrig.display().to_string(),
|
||||||
path_gupax,
|
tmp_dir: "".to_string(),
|
||||||
path_p2pool,
|
updating: Arc::new(Mutex::new(true)),
|
||||||
path_xmrig,
|
prog: Arc::new(Mutex::new(0)),
|
||||||
updating: Arc::new(Mutex::new(false)),
|
msg: Arc::new(Mutex::new("".to_string())),
|
||||||
update_prog: 0,
|
tor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(state: &mut State) -> Result((), Error) {
|
// Get a temporary random folder
|
||||||
|
// for package download contents
|
||||||
|
// Will look like [/tmp/gupax_A1m98FN3fa/] on Unix
|
||||||
|
pub fn get_tmp_dir() -> String {
|
||||||
|
let rand_string: String = thread_rng()
|
||||||
|
.sample_iter(&Alphanumeric)
|
||||||
|
.take(10)
|
||||||
|
.map(char::from)
|
||||||
|
.collect();
|
||||||
|
let tmp = std::env::temp_dir();
|
||||||
|
let tmp = format!("{}{}{}{}", tmp.display(), "/gupax_", rand_string, "/");
|
||||||
|
info!("Update | TMP directory ... {}", tmp);
|
||||||
|
tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The HTTPS client created when Tor is enabled:
|
||||||
|
// TLS implementation | tls-api -> native-tls
|
||||||
|
// Tor implementatoin | arti
|
||||||
|
pub async fn get_tor_client() -> Result<ClientEnum, anyhow::Error> {
|
||||||
|
info!("Update | Creating Tor+HTTPS client...");
|
||||||
|
let tor = TorClient::create_bootstrapped(TorClientConfig::default()).await?;
|
||||||
|
let tls = tls_api_native_tls::TlsConnector::builder()?.build()?;
|
||||||
|
let http = ArtiHttpConnector::new(tor, tls);
|
||||||
|
let client = ClientEnum::Tor(Client::builder().build(http));
|
||||||
|
info!("Update | Tor client ... OK");
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The HTTPS client created when Tor is disabled:
|
||||||
|
// TLS implementation | hyper-tls
|
||||||
|
pub async fn get_client() -> Result<ClientEnum, anyhow::Error> {
|
||||||
|
info!("Update | Creating HTTPS client...");
|
||||||
|
let mut https = hyper_tls::HttpsConnector::new();
|
||||||
|
https.https_only(true);
|
||||||
|
let client = ClientEnum::Https(Client::builder().build(https));
|
||||||
|
info!("Update | HTTPS client ... OK");
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download process:
|
||||||
|
// 0. setup tor, client, http, etc
|
||||||
|
// 1. fill vector with all enums
|
||||||
|
// 2. loop over vec, download metadata
|
||||||
|
// 3. if current == version, remove from vec
|
||||||
|
// 4. loop over vec, download links
|
||||||
|
// 5. extract, upgrade
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
pub async fn start(&mut self) -> Result<(), anyhow::Error> {
|
||||||
|
// Set progress bar
|
||||||
|
*self.msg.lock().unwrap() = MSG_START.to_string();
|
||||||
|
*self.prog.lock().unwrap() = 0;
|
||||||
|
|
||||||
|
// Get temporary directory
|
||||||
|
*self.msg.lock().unwrap() = MSG_TMP.to_string();
|
||||||
|
let tmp_dir = Self::get_tmp_dir();
|
||||||
|
*self.prog.lock().unwrap() = 5;
|
||||||
|
|
||||||
|
// Make Pkg vector
|
||||||
|
*self.msg.lock().unwrap() = MSG_PKG.to_string();
|
||||||
|
let vec = vec![
|
||||||
|
Pkg::new(Gupax, &tmp_dir, self.prog.clone(), self.msg.clone()),
|
||||||
|
Pkg::new(P2pool, &tmp_dir, self.prog.clone(), self.msg.clone()),
|
||||||
|
Pkg::new(Xmrig, &tmp_dir, self.prog.clone(), self.msg.clone()),
|
||||||
|
];
|
||||||
|
let mut handles: Vec<JoinHandle<()>> = vec![];
|
||||||
|
*self.prog.lock().unwrap() = 5;
|
||||||
|
|
||||||
|
// Create Tor/HTTPS client
|
||||||
|
let mut client: ClientEnum;
|
||||||
|
if self.tor {
|
||||||
|
*self.msg.lock().unwrap() = MSG_TOR.to_string();
|
||||||
|
client = Self::get_tor_client().await?;
|
||||||
|
} else {
|
||||||
|
*self.msg.lock().unwrap() = MSG_HTTPS.to_string();
|
||||||
|
client = Self::get_client().await?;
|
||||||
|
}
|
||||||
|
*self.prog.lock().unwrap() = 10;
|
||||||
|
|
||||||
|
// loop for metadata
|
||||||
|
for pkg in vec.iter() {
|
||||||
|
info!("Update | Metadata | Starting ... {}", pkg.name);
|
||||||
|
let name = pkg.name.clone();
|
||||||
|
let version = Arc::clone(&pkg.version);
|
||||||
|
let request = hyper::Request::builder()
|
||||||
|
.method("GET")
|
||||||
|
.uri(pkg.link_metadata)
|
||||||
|
.header(hyper::header::USER_AGENT, hyper::header::HeaderValue::from_static("Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0"))
|
||||||
|
.body(Body::empty())?;
|
||||||
|
let client = client.clone();
|
||||||
|
let handle: JoinHandle<()> = tokio::spawn(async move {
|
||||||
|
Pkg::get_tor_response(name, version, client, request).await;
|
||||||
|
});
|
||||||
|
handles.push(handle);
|
||||||
|
}
|
||||||
|
for handle in handles {
|
||||||
|
handle.await?;
|
||||||
|
}
|
||||||
|
info!("Update | Metadata ... OK\n");
|
||||||
|
Ok(())
|
||||||
|
|
||||||
|
//----------------------------------------------
|
||||||
|
//
|
||||||
|
// // loop for download
|
||||||
|
// let mut handles: Vec<JoinHandle<()>> = vec![];
|
||||||
|
// for pkg in vec.iter() {
|
||||||
|
// let name = pkg.name.clone();
|
||||||
|
// let bytes = Arc::clone(&pkg.bytes);
|
||||||
|
// let version = pkg.version.lock().unwrap();
|
||||||
|
// let link;
|
||||||
|
// if pkg.name == Name::Xmrig {
|
||||||
|
// link = pkg.link_prefix.clone() + &version + &pkg.link_suffix_1 + &version[1..] + &pkg.link_suffix_2;
|
||||||
|
// } else {
|
||||||
|
// link = pkg.link_prefix.clone() + &version + &pkg.link_suffix_1 + &version + &pkg.link_suffix_2;
|
||||||
|
// }
|
||||||
|
// println!("download: {:#?} | {}", pkg.name, link);
|
||||||
|
// let request = Client::get(&client.clone(), &link);
|
||||||
|
// let handle: JoinHandle<()> = tokio::spawn(async move {
|
||||||
|
// get_bytes(request, bytes, name).await;
|
||||||
|
// });
|
||||||
|
// handles.push(handle);
|
||||||
|
// }
|
||||||
|
// for handle in handles {
|
||||||
|
// handle.await.unwrap();
|
||||||
|
// }
|
||||||
|
// println!("download ... OK\n");
|
||||||
|
//
|
||||||
|
// // extract
|
||||||
|
// let TMP = num();
|
||||||
|
// std::fs::create_dir(&TMP).unwrap();
|
||||||
|
// for pkg in vec.iter() {
|
||||||
|
// let name = TMP.to_string() + &pkg.name.to_string();
|
||||||
|
// println!("extract: {:#?} | {}", pkg.name, name);
|
||||||
|
// if pkg.name == Name::Gupax {
|
||||||
|
// std::fs::OpenOptions::new().mode(0o700).create(true).write(true).open(&name);
|
||||||
|
// std::fs::write(name, pkg.bytes.lock().unwrap().as_ref()).unwrap();
|
||||||
|
// } else {
|
||||||
|
// std::fs::create_dir(&name).unwrap();
|
||||||
|
// tar::Archive::new(flate2::read::GzDecoder::new(pkg.bytes.lock().unwrap().as_ref())).unpack(name).unwrap();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// println!("extract ... OK");
|
||||||
|
//
|
||||||
|
//async fn get_bytes(request: RequestBuilder, bytes: Arc<Mutex<bytes::Bytes>>, name: Name) {
|
||||||
|
// *bytes.lock().unwrap() = request.send().await.unwrap().bytes().await.unwrap();
|
||||||
|
// println!("{} download ... OK", name);
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//async fn func(request: RequestBuilder, version: Arc<Mutex<String>>, name: Name) {
|
||||||
|
// let response = request.send().await.unwrap().bytes().await.unwrap();
|
||||||
|
//
|
||||||
|
// let mut bytes = flate2::read::GzDecoder::new(response.as_ref());
|
||||||
|
// let mut response = String::new();
|
||||||
|
// bytes.read_to_string(&mut response).unwrap();
|
||||||
|
//
|
||||||
|
// let response: Version = serde_json::from_str(&response).unwrap();
|
||||||
|
// *version.lock().unwrap() = response.tag_name.clone();
|
||||||
|
// println!("{} {} ... OK", name, response.tag_name);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper type around Tor/HTTPS client
|
||||||
|
enum ClientEnum {
|
||||||
|
Tor(Client<ArtiHttpConnector<tor_rtcompat::PreferredRuntime, tls_api_native_tls::TlsConnector>>),
|
||||||
|
Https(Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Pkg struct/impl
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Pkg {
|
||||||
|
name: Name,
|
||||||
|
link_metadata: &'static str,
|
||||||
|
link_prefix: &'static str,
|
||||||
|
link_suffix: &'static str,
|
||||||
|
link_extension: &'static str,
|
||||||
|
tmp_dir: String,
|
||||||
|
update_prog: Arc<Mutex<u8>>,
|
||||||
|
update_msg: Arc<Mutex<String>>,
|
||||||
|
bytes: Arc<Mutex<bytes::Bytes>>,
|
||||||
|
version: Arc<Mutex<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pkg {
|
||||||
|
pub fn new(name: Name, tmp_dir: &String, update_prog: Arc<Mutex<u8>>, update_msg: Arc<Mutex<String>>) -> Self {
|
||||||
|
let link_metadata = match name {
|
||||||
|
Gupax => GUPAX_METADATA,
|
||||||
|
P2pool => P2POOL_METADATA,
|
||||||
|
Xmrig => XMRIG_METADATA,
|
||||||
|
};
|
||||||
|
let link_prefix = match name {
|
||||||
|
Gupax => GUPAX_PREFIX,
|
||||||
|
P2pool => P2POOL_PREFIX,
|
||||||
|
Xmrig => XMRIG_PREFIX,
|
||||||
|
};
|
||||||
|
let link_suffix = match name {
|
||||||
|
Gupax => GUPAX_SUFFIX,
|
||||||
|
P2pool => P2POOL_SUFFIX,
|
||||||
|
Xmrig => XMRIG_SUFFIX,
|
||||||
|
};
|
||||||
|
let link_extension = match name {
|
||||||
|
Gupax => GUPAX_EXTENSION,
|
||||||
|
P2pool => P2POOL_EXTENSION,
|
||||||
|
Xmrig => XMRIG_EXTENSION,
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
link_metadata,
|
||||||
|
link_prefix,
|
||||||
|
link_suffix,
|
||||||
|
link_extension,
|
||||||
|
tmp_dir: tmp_dir.to_string(),
|
||||||
|
update_prog,
|
||||||
|
update_msg,
|
||||||
|
bytes: Arc::new(Mutex::new(bytes::Bytes::new())),
|
||||||
|
version: Arc::new(Mutex::new(String::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate GET request based off input URI + fake user agent
|
||||||
|
pub async fn get_request(self) -> Result<Request<Body>, anyhow::Error> {
|
||||||
|
let user_agent = FAKE_USER_AGENT[thread_rng().gen_range(0..50)];
|
||||||
|
let request = Request::builder()
|
||||||
|
.method("GET")
|
||||||
|
.uri(self.link_metadata)
|
||||||
|
.header(hyper::header::USER_AGENT, HeaderValue::from_static(user_agent))
|
||||||
|
.body(Body::empty())?;
|
||||||
|
Ok(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get response using [Tor client] + [request]
|
||||||
|
// and change the [version] under an Arc<Mutex>
|
||||||
|
pub async fn get_tor_response(name: Name, version: Arc<Mutex<String>>, client: Client<ArtiHttpConnector<tor_rtcompat::PreferredRuntime, tls_api_native_tls::TlsConnector>>, request: Request<Body>) -> Result<(), Error> {
|
||||||
|
let mut response = client.request(request).await?;
|
||||||
|
let body = hyper::body::to_bytes(response.body_mut()).await?;
|
||||||
|
let body: Version = serde_json::from_slice(&body)?;
|
||||||
|
*version.lock().unwrap() = body.tag_name.clone();
|
||||||
|
info!("Update | Metadata | {} {} ... OK", name, body.tag_name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same thing, but without Tor.
|
||||||
|
pub async fn get_response(name: Name, version: Arc<Mutex<String>>, client: Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>, request: Request<Body>) -> Result<(), Error> {
|
||||||
|
let mut response = client.request(request).await?;
|
||||||
|
let body = hyper::body::to_bytes(response.body_mut()).await?;
|
||||||
|
let body: Version = serde_json::from_slice(&body)?;
|
||||||
|
*version.lock().unwrap() = body.tag_name.clone();
|
||||||
|
info!("Update | Metadata | {} {} ... OK", name, body.tag_name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This inherits the value of [tag_name] from GitHub's JSON API
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct TagName {
|
struct Version {
|
||||||
tag_name: String,
|
tag_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
enum Error {
|
pub enum Name {
|
||||||
Io(std::io::Error),
|
|
||||||
Serialize(toml::ser::Error),
|
|
||||||
Deserialize(toml::de::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
enum Package {
|
|
||||||
Gupax,
|
Gupax,
|
||||||
P2pool,
|
P2pool,
|
||||||
Xmrig,
|
Xmrig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Name {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue