Skip to content

Commit

Permalink
fix: login info lost for mining (#1426)
Browse files Browse the repository at this point in the history
* Moves start/stop mining to its own file
* Removes client side persist of airdrop user token and saves it on the
be side
* Adds restart mm proxy logic on the be side when user tokens are
updated

---------

Co-authored-by: Balazs Sevecsek <[email protected]>
  • Loading branch information
PanchoBubble and BalazsSevecsek authored Jan 24, 2025
1 parent 8c99ccd commit dc8b962
Show file tree
Hide file tree
Showing 12 changed files with 372 additions and 155 deletions.
118 changes: 118 additions & 0 deletions src-tauri/src/airdrop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2024. The Tari Project
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
// following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
// disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
// products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use log::{info, warn};
use serde::{Deserialize, Serialize};

use crate::{mm_proxy_adapter::MergeMiningProxyConfig, UniverseAppState};

const LOG_TARGET: &str = "tari::universe::airdrop";

#[derive(Debug, Deserialize, Serialize)]
pub struct AirdropAccessToken {
pub exp: u64,
pub iat: i32,
pub id: String,
pub provider: String,
pub role: String,
pub scope: String,
}

pub fn decode_jwt_claims(t: &str) -> Option<AirdropAccessToken> {
let key = DecodingKey::from_secret(&[]);
let mut validation = Validation::new(Algorithm::HS256);
validation.insecure_disable_signature_validation();

match decode::<AirdropAccessToken>(t, &key, &validation) {
Ok(data) => Some(data.claims),
Err(e) => {
warn!(target: LOG_TARGET,"Error decoding access token: {:?}", e);
None
}
}
}

pub fn decode_jwt_claims_without_exp(t: &str) -> Option<AirdropAccessToken> {
let key = DecodingKey::from_secret(&[]);
let mut validation = Validation::new(Algorithm::HS256);
validation.insecure_disable_signature_validation();
validation.validate_exp = false;

match decode::<AirdropAccessToken>(t, &key, &validation) {
Ok(data) => Some(data.claims),
Err(e) => {
warn!(target: LOG_TARGET,"Error decoding access token without exp: {:?}", e);
None
}
}
}

pub async fn validate_jwt(airdrop_access_token: Option<String>) -> Option<String> {
airdrop_access_token.and_then(|t| {
let claims = decode_jwt_claims(&t);

let now = std::time::SystemTime::now();
let now_unix = now
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();

if let Some(claims) = claims {
if claims.exp < now_unix {
warn!(target: LOG_TARGET,"Access token has expired");
None
} else {
Some(t)
}
} else {
None
}
})
}

pub async fn restart_mm_proxy_with_new_telemetry_id(
state: tauri::State<'_, UniverseAppState>,
) -> Result<(), String> {
info!(target: LOG_TARGET, "Restarting mm_proxy");
let telemetry_id = state
.telemetry_manager
.read()
.await
.get_unique_string()
.await;
let mm_proxy_manager_config = state
.mm_proxy_manager
.config()
.await
.ok_or("mm proxy config could not be found")?;
let _unused = state
.mm_proxy_manager
.change_config(MergeMiningProxyConfig {
coinbase_extra: telemetry_id.clone(),
..mm_proxy_manager_config
})
.await
.map_err(|e| e.to_string());
info!(target: LOG_TARGET, "mm_proxy restarted");
Ok(())
}
26 changes: 26 additions & 0 deletions src-tauri/src/app_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ pub struct AppConfigFromFile {
p2pool_stats_server_port: Option<u16>,
#[serde(default = "default_false")]
pre_release: bool,
#[serde(default)]
airdrop_tokens: Option<AirdropTokens>,
}

impl Default for AppConfigFromFile {
Expand Down Expand Up @@ -154,6 +156,7 @@ impl Default for AppConfigFromFile {
show_experimental_settings: false,
p2pool_stats_server_port: default_p2pool_stats_server_port(),
pre_release: false,
airdrop_tokens: None,
}
}
}
Expand All @@ -165,6 +168,12 @@ pub enum DisplayMode {
Light,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AirdropTokens {
pub token: String,
pub refresh_token: String,
}

impl DisplayMode {
pub fn from_str(s: &str) -> Option<DisplayMode> {
match s {
Expand Down Expand Up @@ -266,6 +275,7 @@ pub(crate) struct AppConfig {
show_experimental_settings: bool,
p2pool_stats_server_port: Option<u16>,
pre_release: bool,
airdrop_tokens: Option<AirdropTokens>,
}

impl AppConfig {
Expand Down Expand Up @@ -310,6 +320,7 @@ impl AppConfig {
keyring_accessed: false,
p2pool_stats_server_port: default_p2pool_stats_server_port(),
pre_release: false,
airdrop_tokens: None,
}
}

Expand Down Expand Up @@ -387,6 +398,7 @@ impl AppConfig {
self.show_experimental_settings = config.show_experimental_settings;
self.p2pool_stats_server_port = config.p2pool_stats_server_port;
self.pre_release = config.pre_release;
self.airdrop_tokens = config.airdrop_tokens;

KEYRING_ACCESSED.store(
config.keyring_accessed,
Expand Down Expand Up @@ -506,6 +518,19 @@ impl AppConfig {
self.custom_max_gpu_usage.clone()
}

pub fn airdrop_tokens(&self) -> Option<AirdropTokens> {
self.airdrop_tokens.clone()
}

pub async fn set_airdrop_tokens(
&mut self,
airdrop_tokens: Option<AirdropTokens>,
) -> Result<(), anyhow::Error> {
self.airdrop_tokens = airdrop_tokens;
self.update_config_file().await?;
Ok(())
}

pub async fn set_max_gpu_usage(
&mut self,
custom_max_gpu_usage: Vec<GpuThreads>,
Expand Down Expand Up @@ -786,6 +811,7 @@ impl AppConfig {
show_experimental_settings: self.show_experimental_settings,
p2pool_stats_server_port: self.p2pool_stats_server_port,
pre_release: self.pre_release,
airdrop_tokens: self.airdrop_tokens.clone(),
};
let config = serde_json::to_string(config)?;
debug!(target: LOG_TARGET, "Updating config file: {:?} {:?}", file, self.clone());
Expand Down
4 changes: 0 additions & 4 deletions src-tauri/src/app_in_memory_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ const TELEMETRY_API_URL: &str =
pub struct AppInMemoryConfig {
pub airdrop_url: String,
pub airdrop_api_url: String,
pub airdrop_access_token: Option<String>,
pub telemetry_api_url: String,
}

Expand All @@ -67,7 +66,6 @@ impl Default for AppInMemoryConfig {
AppInMemoryConfig {
airdrop_url: "https://airdrop.tari.com".into(),
airdrop_api_url: "https://ut.tari.com".into(),
airdrop_access_token: None,
telemetry_api_url: "https://ut.tari.com/push".into(),
}
}
Expand Down Expand Up @@ -112,15 +110,13 @@ impl AppInMemoryConfig {
return AppInMemoryConfig {
airdrop_url: AIRDROP_BASE_URL.into(),
airdrop_api_url: AIRDROP_API_BASE_URL.into(),
airdrop_access_token: None,
telemetry_api_url: TELEMETRY_API_URL.into(),
};

#[cfg(feature = "airdrop-local")]
return AppInMemoryConfig {
airdrop_url: "http://localhost:4000".into(),
airdrop_api_url: "http://localhost:3004".into(),
airdrop_access_token: None,
telemetry_api_url: "http://localhost:3004".into(),
};

Expand Down
95 changes: 91 additions & 4 deletions src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use crate::app_config::{AppConfig, GpuThreads};
use crate::app_config::{AirdropTokens, AppConfig, GpuThreads};
use crate::app_in_memory_config::{
get_der_encode_pub_key, get_websocket_key, AirdropInMemoryConfig,
};
Expand All @@ -40,7 +40,7 @@ use crate::tor_adapter::TorConfig;
use crate::utils::shutdown_utils::stop_all_processes;
use crate::wallet_adapter::{TransactionInfo, WalletBalance};
use crate::wallet_manager::WalletManagerError;
use crate::{node_adapter, UniverseAppState, APPLICATION_FOLDER_ID};
use crate::{airdrop, node_adapter, UniverseAppState, APPLICATION_FOLDER_ID};

use base64::prelude::*;
use keyring::Entry;
Expand Down Expand Up @@ -704,6 +704,20 @@ pub async fn get_tor_entry_guards(
Ok(res)
}

#[tauri::command]
pub async fn get_airdrop_tokens(
_window: tauri::Window,
state: tauri::State<'_, UniverseAppState>,
_app: tauri::AppHandle,
) -> Result<Option<AirdropTokens>, String> {
let timer = Instant::now();
let airdrop_access_token = state.config.read().await.airdrop_tokens();
if timer.elapsed() > MAX_ACCEPTABLE_COMMAND_TIME {
warn!(target: LOG_TARGET, "get_airdrop_tokens took too long: {:?}", timer.elapsed());
}
Ok(airdrop_access_token)
}

#[tauri::command]
pub async fn get_coinbase_transactions(
state: tauri::State<'_, UniverseAppState>,
Expand Down Expand Up @@ -1406,6 +1420,73 @@ pub async fn set_visual_mode<'r>(
Ok(())
}

#[allow(clippy::too_many_lines)]
#[tauri::command]
pub async fn set_airdrop_tokens<'r>(
airdrop_tokens: Option<AirdropTokens>,
state: tauri::State<'_, UniverseAppState>,
app: tauri::AppHandle,
) -> Result<(), String> {
let old_tokens = state.config.clone().read().await.airdrop_tokens();
let old_id = old_tokens.clone().and_then(|tokens| {
airdrop::decode_jwt_claims_without_exp(&tokens.token).map(|claim| claim.id)
});
let new_id = airdrop_tokens.clone().and_then(|tokens| {
airdrop::decode_jwt_claims_without_exp(&tokens.token).map(|claim| claim.id)
});

let user_id_changed = old_id != new_id;

let mut app_config_lock = state.config.write().await;
app_config_lock
.set_airdrop_tokens(airdrop_tokens)
.await
.map_err(|e| e.to_string())?;
drop(app_config_lock);

info!(target: LOG_TARGET, "New Airdrop tokens saved, user id changed:{:?}", user_id_changed);
if user_id_changed {
let currently_mining = {
let node_status = state.base_node_latest_status.borrow().clone();
let cpu_miner = state.cpu_miner.read().await;
let cpu_mining_status = match cpu_miner
.status(
node_status.randomx_network_hashrate,
node_status.block_reward,
)
.await
.map_err(|e| e.to_string())
{
Ok(cpu) => cpu,
Err(e) => {
warn!(target: LOG_TARGET, "Error getting cpu miner status: {:?}", e);
state
.is_getting_miner_metrics
.store(false, Ordering::SeqCst);
return Err(e);
}
};
let gpu_mining_status = state.gpu_latest_status.borrow().clone();
cpu_mining_status.is_mining || gpu_mining_status.is_mining
};

if currently_mining {
stop_mining(state.clone())
.await
.map_err(|e| e.to_string())?;

airdrop::restart_mm_proxy_with_new_telemetry_id(state.clone()).await?;

start_mining(state.clone(), app.clone())
.await
.map_err(|e| e.to_string())?;
} else {
airdrop::restart_mm_proxy_with_new_telemetry_id(state.clone()).await?;
}
}
Ok(())
}

#[allow(clippy::too_many_lines)]
#[tauri::command]
pub async fn start_mining<'r>(
Expand All @@ -1420,6 +1501,8 @@ pub async fn start_mining<'r>(
let mode = config.mode();
let custom_cpu_usage = config.custom_cpu_usage();
let custom_gpu_usage = config.custom_gpu_usage();
let cpu_miner_running = state.cpu_miner.read().await.is_running().await;
let gpu_miner_running = state.gpu_miner.read().await.is_running().await;

let cpu_miner_config = state.cpu_miner_config.read().await;
let tari_address = cpu_miner_config.tari_address.clone();
Expand All @@ -1431,7 +1514,8 @@ pub async fn start_mining<'r>(
.await
.get_unique_string()
.await;
if cpu_mining_enabled {

if cpu_mining_enabled && !cpu_miner_running {
let mm_proxy_port = state
.mm_proxy_manager
.get_monero_port()
Expand Down Expand Up @@ -1476,7 +1560,7 @@ pub async fn start_mining<'r>(
let gpu_available = state.gpu_miner.read().await.is_gpu_mining_available();
info!(target: LOG_TARGET, "Gpu availability {:?} gpu_mining_enabled {}", gpu_available.clone(), gpu_mining_enabled);

if gpu_mining_enabled && gpu_available {
if gpu_mining_enabled && gpu_available && !gpu_miner_running {
info!(target: LOG_TARGET, "1. Starting gpu miner");
// let tari_address = state.cpu_miner_config.read().await.tari_address.clone();
// let p2pool_enabled = state.config.read().await.p2pool_enabled();
Expand Down Expand Up @@ -1549,6 +1633,7 @@ pub async fn stop_mining<'r>(state: tauri::State<'_, UniverseAppState>) -> Resul
.stop()
.await
.map_err(|e| e.to_string())?;
info!(target:LOG_TARGET, "cpu miner stopped");

state
.gpu_miner
Expand All @@ -1557,6 +1642,8 @@ pub async fn stop_mining<'r>(state: tauri::State<'_, UniverseAppState>) -> Resul
.stop()
.await
.map_err(|e| e.to_string())?;
info!(target:LOG_TARGET, "gpu miner stopped");

if timer.elapsed() > MAX_ACCEPTABLE_COMMAND_TIME {
warn!(target: LOG_TARGET, "stop_mining took too long: {:?}", timer.elapsed());
}
Expand Down
Loading

0 comments on commit dc8b962

Please sign in to comment.