Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: login info lost for mining #1426

Merged
merged 22 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ae1ff92
fix: proxy not getting restarted at stop mining.
BalazsSevecsek Jan 20, 2025
bc31681
fix: change airdrop token handling logic.
BalazsSevecsek Jan 21, 2025
da6ed39
fix: logout logic and fe logic around login is fixed
BalazsSevecsek Jan 22, 2025
ab9e31c
updates
PanchoBubble Jan 22, 2025
82011cc
feat: add airdrop access token
BalazsSevecsek Jan 22, 2025
dcdc195
adds type
PanchoBubble Jan 22, 2025
a08eed0
fix: storing refresh token as well.
BalazsSevecsek Jan 22, 2025
64cff3e
fix: make the frontend handle the same format for the tokens
BalazsSevecsek Jan 22, 2025
ca47c67
Updates on load config
PanchoBubble Jan 22, 2025
a10dc78
fix: deadlock at starting up
BalazsSevecsek Jan 22, 2025
b21402d
removes logs
PanchoBubble Jan 22, 2025
dc5f363
removes logs
PanchoBubble Jan 22, 2025
d0818df
Merge branch 'main' into fix-login-info-lost-for-mining
BalazsSevecsek Jan 23, 2025
7ca1a8e
choir: remove unnecessary imports
BalazsSevecsek Jan 23, 2025
2da6049
fix: remove unnecessary comments
BalazsSevecsek Jan 23, 2025
ea397ae
fix: add copyright license to new file
BalazsSevecsek Jan 23, 2025
ec8835f
fix: add license to airdrop.rs
BalazsSevecsek Jan 23, 2025
4825e06
checks for persisted tokens
PanchoBubble Jan 23, 2025
ed73f0d
Merge remote-tracking branch 'prod/main' into fix-login-info-lost-for…
PanchoBubble Jan 23, 2025
d11f552
fix: add console error when fetching airdrop token was not successful.
BalazsSevecsek Jan 24, 2025
4f25107
fix: move commands_internal back to commands
BalazsSevecsek Jan 24, 2025
ff439a0
choir: Merge branch 'main' into fix-login-info-lost-for-mining
BalazsSevecsek Jan 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading