From 1ecd37b99d4115cac1a1bfb2b7ab36183e14132f Mon Sep 17 00:00:00 2001
From: hinto-janaiyo <hinto.janaiyo@protonmail.com>
Date: Sat, 19 Nov 2022 09:39:26 -0500
Subject: [PATCH] update: recreate tor client on failure in metadata loop

---
 Cargo.toml    |  5 ++++-
 README.md     |  9 +++++----
 src/disk.rs   | 12 ++++++------
 src/gupax.rs  |  3 +--
 src/main.rs   |  3 +--
 src/update.rs | 35 +++++++++++++++++++++--------------
 6 files changed, 38 insertions(+), 29 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 888f0a3..3c4ec49 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,7 +37,6 @@ log = "0.4.17"
 monero = "0.18.0"
 num_cpus = "1.13.1"
 num-format = "0.4.0"
-openssl = { version = "*", features = ["vendored"] }
 rand = "0.8.5"
 regex = "1.6.0"
 reqwest = { version = "0.11.12", features = ["blocking", "json"] }
@@ -57,6 +56,10 @@ walkdir = "2.3.2"
 [target.'cfg(unix)'.dependencies]
 tar = "0.4.38"
 
+# Bundle OpenSSL for Linux
+[target.'cfg(linux)'.dependencies]
+openssl = { version = "*", features = ["vendored"] }
+
 # Windows dependencies
 [target.'cfg(windows)'.dependencies]
 zip = "0.6.3"
diff --git a/README.md b/README.md
index 477fe06..3baa6aa 100644
--- a/README.md
+++ b/README.md
@@ -67,11 +67,12 @@ With Monero GUI managing the Monero node on one side and Gupax managing P2Pool/X
 | Monerujo       | nodex.monerujo.io                | 18081    |
 | Rino           | node.community.rino.io           | 18081    |
 | Seth           | node.sethforprivacy.com          | 18089    |
-| Singapore      | node.supportxmr.com              | 18081    |
-| SupportXmr     | node.supportxmr.ir               | 18081    |
-| SupportXmrIr   | singapore.node.xmr.pm            | 18089    |
+| Singapore      | singapore.node.xmr               | 18081    |
+| SupportXmr     | node.supportxmr.com              | 18081    |
+| SupportXmrIr   | node.supportxmr.ir               | 18089    |
 | XmrVsBeast     | p2pmd.xmrvsbeast.com             | 18081    |
-**Note: All have ZMQ port on 18083**
+
+*Note: All have ZMQ port on 18083*
 
 ## Demo
 https://user-images.githubusercontent.com/101352116/194763334-d8e936c9-a71e-474e-ac65-3a339b96a9d2.mp4
diff --git a/src/disk.rs b/src/disk.rs
index c4a6a89..b7b9666 100644
--- a/src/disk.rs
+++ b/src/disk.rs
@@ -171,9 +171,9 @@ impl State {
 				address: String::with_capacity(95),
 			},
 			version: Arc::new(Mutex::new(Version {
-				gupax: Arc::new(Mutex::new(GUPAX_VERSION.to_string())),
-				p2pool: Arc::new(Mutex::new(P2POOL_VERSION.to_string())),
-				xmrig: Arc::new(Mutex::new(XMRIG_VERSION.to_string())),
+				gupax: GUPAX_VERSION.to_string(),
+				p2pool: P2POOL_VERSION.to_string(),
+				xmrig: XMRIG_VERSION.to_string(),
 			})),
 		}
 	}
@@ -552,7 +552,7 @@ pub struct Xmrig {
 
 #[derive(Clone,Debug,Deserialize,Serialize)]
 pub struct Version {
-	pub gupax: Arc<Mutex<String>>,
-	pub p2pool: Arc<Mutex<String>>,
-	pub xmrig: Arc<Mutex<String>>,
+	pub gupax: String,
+	pub p2pool: String,
+	pub xmrig: String,
 }
diff --git a/src/gupax.rs b/src/gupax.rs
index afdd2ad..66734f5 100644
--- a/src/gupax.rs
+++ b/src/gupax.rs
@@ -72,13 +72,12 @@ impl Gupax {
 						update.lock().unwrap().path_xmrig = og.lock().unwrap().gupax.absolute_xmrig_path.display().to_string();
 						update.lock().unwrap().tor = og.lock().unwrap().gupax.update_via_tor;
 						let og = Arc::clone(&og);
-						let og_ver = Arc::clone(&og.lock().unwrap().version);
 						let state_ver = Arc::clone(&state_ver);
 						let update = Arc::clone(&update);
 						let update_thread = Arc::clone(&update);
 						thread::spawn(move|| {
 							info!("Spawning update thread...");
-							match Update::start(update_thread, og_ver.clone(), state_ver.clone()) {
+							match Update::start(update_thread, og.clone(), state_ver.clone()) {
 								Err(e) => {
 									info!("Update ... FAIL ... {}", e);
 									*update.lock().unwrap().msg.lock().unwrap() = format!("{} | {}", MSG_FAILED, e);
diff --git a/src/main.rs b/src/main.rs
index 107c2d0..24ebce1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -384,13 +384,12 @@ fn init_auto(app: &App) {
 		app.update.lock().unwrap().path_xmrig = path_xmrig;
 		app.update.lock().unwrap().tor = tor;
 		let og = Arc::clone(&app.og);
-		let og_ver = Arc::clone(&app.og.lock().unwrap().version);
 		let state_ver = Arc::clone(&app.state.version);
 		let update = Arc::clone(&app.update);
 		let update_thread = Arc::clone(&app.update);
 		thread::spawn(move|| {
 			info!("Spawning update thread...");
-			match Update::start(update_thread, og_ver.clone(), state_ver.clone()) {
+			match Update::start(update_thread, og.clone(), state_ver.clone()) {
 				Err(e) => {
 					info!("Update ... FAIL ... {}", e);
 					*update.lock().unwrap().msg.lock().unwrap() = format!("{} | {}", MSG_FAILED, e);
diff --git a/src/update.rs b/src/update.rs
index 05da782..b005fc5 100644
--- a/src/update.rs
+++ b/src/update.rs
@@ -270,11 +270,12 @@ impl Update {
 	//     ClientEnum::Tor(T)   => get_reponse(... T ...)
 	//     ClientEnum::Https(H) => get_reponse(... H ...)
 	//
-	pub async fn get_client(tor: bool) -> Result<ClientEnum, anyhow::Error> {
+	pub fn get_client(tor: bool) -> Result<ClientEnum, anyhow::Error> {
 		if tor {
-			// This one below is non-async, but it doesn't bootstrap immediately.
-//			let tor = TorClient::builder().bootstrap_behavior(arti_client::BootstrapBehavior::OnDemand).create_unbootstrapped()?;
-			let tor = TorClient::create_bootstrapped(TorClientConfig::default()).await?;
+			// This one below is non-async, and doesn't bootstrap immediately.
+			let tor = TorClient::builder().bootstrap_behavior(arti_client::BootstrapBehavior::OnDemand).create_unbootstrapped()?;
+			// Below is async, bootstraps immediately but has issues when recreating the circuit
+//			let tor = TorClient::create_bootstrapped(TorClientConfig::default()).await?;
 			let tls = tls_api_native_tls::TlsConnector::builder()?.build()?;
 		    let connector = ArtiHttpConnector::new(tor, tls);
 			let client = ClientEnum::Tor(Client::builder().build(connector));
@@ -296,7 +297,7 @@ impl Update {
 	// 5. extract, upgrade
 
 	#[tokio::main]
-	pub async fn start(update: Arc<Mutex<Self>>, og_ver: Arc<Mutex<Version>>, state_ver: Arc<Mutex<Version>>) -> Result<(), anyhow::Error> {
+	pub async fn start(update: Arc<Mutex<Self>>, og: Arc<Mutex<State>>, state_ver: Arc<Mutex<Version>>) -> Result<(), anyhow::Error> {
 		//---------------------------------------------------------------------------------------------------- Init
 		*update.lock().unwrap().updating.lock().unwrap() = true;
 		// Set timer
@@ -337,7 +338,7 @@ impl Update {
 		let prog = *update.lock().unwrap().prog.lock().unwrap();
 		info!("Update | {}", update.lock().unwrap().msg.lock().unwrap());
 		let tor = update.lock().unwrap().tor;
-		let client = Self::get_client(tor).await?;
+		let mut client = Self::get_client(tor)?;
 		*update.lock().unwrap().prog.lock().unwrap() += 5.0;
 		info!("Update | Init ... OK ... {}%", prog);
 
@@ -351,7 +352,7 @@ impl Update {
 		// 3. Iterate over all [pkg.new_ver], check if empty
 		// 4. If not empty, move [pkg] to different vec
 		// 5. At end, if original vec isn't empty, that means something failed
-		// 6. Redo loop [3] times, with the original vec (that now only has the failed pkgs)
+		// 6. Redo loop [3] times (rebuild circuit if using Tor), with the original vec (that now only has the failed pkgs)
 		//
 		// This logic was originally in the [Pkg::get_metadata()]
 		// function itself but for some reason, it was getting skipped over,
@@ -403,6 +404,12 @@ impl Update {
 				vec.remove(index);
 			}
 			if vec.is_empty() { break }
+			// Some Tor exit nodes seem to be blocked by GitHub's API,
+			// so recreate the circuit every loop.
+			if tor {
+				info!("Update | Recreating Tor client...");
+				client = Self::get_client(tor)?;
+			}
 		}
 		if vec.is_empty() {
 			info!("Update | Metadata ... OK ... {}%", update.lock().unwrap().prog.lock().unwrap());
@@ -423,21 +430,21 @@ impl Update {
 			let name;
 			match pkg.name {
 				Gupax  => {
-					old_ver = og_ver.lock().unwrap().gupax.lock().unwrap().to_string();
 					// Compare against the built-in compiled version as well as a in-memory version
 					// that gets updated during an update. This prevents the updater always thinking
 					// there's a new Gupax update since the user didnt restart and is still technically
 					// using the old version (even though the underlying binary was updated).
+					old_ver = og.lock().unwrap().version.lock().unwrap().gupax.clone();
 					diff = old_ver != new_ver && GUPAX_VERSION != new_ver;
 					name = "Gupax";
 				}
 				P2pool => {
-					old_ver = og_ver.lock().unwrap().p2pool.lock().unwrap().to_string();
+					old_ver = og.lock().unwrap().version.lock().unwrap().p2pool.clone();
 					diff = old_ver != new_ver;
 					name = "P2Pool";
 				}
 				Xmrig  => {
-					old_ver = og_ver.lock().unwrap().xmrig.lock().unwrap().to_string();
+					old_ver = og.lock().unwrap().version.lock().unwrap().xmrig.clone();
 					diff = old_ver != new_ver;
 					name = "XMRig";
 				}
@@ -577,7 +584,7 @@ impl Update {
 					std::fs::rename(&path, tmp_windows)?;
 					info!("Update | Moving [{}] -> [{}]", entry.path().display(), path);
 					std::fs::rename(entry.path(), path)?;
-					*og_ver.lock().unwrap().gupax.lock().unwrap() = Pkg::get_new_pkg_version(Gupax, &vec4)?;
+					og.lock().unwrap().version.lock().unwrap().gupax = Pkg::get_new_pkg_version(Gupax, &vec4)?;
 					*update.lock().unwrap().prog.lock().unwrap() += (5.0 / pkg_amount).round();
 				},
 				P2POOL_BINARY => {
@@ -586,7 +593,7 @@ impl Update {
 					info!("Update | Moving [{}] -> [{}]", entry.path().display(), path.display());
 					std::fs::create_dir_all(path.parent().ok_or(anyhow::Error::msg("P2Pool path failed"))?)?;
 					std::fs::rename(entry.path(), path)?;
-					*og_ver.lock().unwrap().p2pool.lock().unwrap() = Pkg::get_new_pkg_version(P2pool, &vec4)?;
+					og.lock().unwrap().version.lock().unwrap().p2pool = Pkg::get_new_pkg_version(P2pool, &vec4)?;
 					*update.lock().unwrap().prog.lock().unwrap() += (5.0 / pkg_amount).round();
 				},
 				XMRIG_BINARY => {
@@ -595,7 +602,7 @@ impl Update {
 					info!("Update | Moving [{}] -> [{}]", entry.path().display(), path.display());
 					std::fs::create_dir_all(path.parent().ok_or(anyhow::Error::msg("XMRig path failed"))?)?;
 					std::fs::rename(entry.path(), path)?;
-					*og_ver.lock().unwrap().xmrig.lock().unwrap() = Pkg::get_new_pkg_version(Xmrig, &vec4)?;
+					og.lock().unwrap().version.lock().unwrap().xmrig = Pkg::get_new_pkg_version(Xmrig, &vec4)?;
 					*update.lock().unwrap().prog.lock().unwrap() += (5.0 / pkg_amount).round();
 				},
 				_ => (),
@@ -611,7 +618,7 @@ impl Update {
 		let seconds = now.elapsed().as_secs();
 		info!("Update | Seconds elapsed ... [{}s]", seconds);
 		match seconds {
-			0 => *update.lock().unwrap().msg.lock().unwrap() = format!("{}! Took 0 seconds... Do you have 10Gbit internet or something...?!{}", MSG_SUCCESS, new_pkgs),
+			0 => *update.lock().unwrap().msg.lock().unwrap() = format!("{}! Took 0 seconds... What...?!{}", MSG_SUCCESS, new_pkgs),
 			1 => *update.lock().unwrap().msg.lock().unwrap() = format!("{}! Took 1 second... Wow!{}", MSG_SUCCESS, new_pkgs),
 			_ => *update.lock().unwrap().msg.lock().unwrap() = format!("{}! Took {} seconds.{}", MSG_SUCCESS, seconds, new_pkgs),
 		}