Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 1 addition & 39 deletions clients/cli/src/analytics.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use crate::environment::Environment;
use crate::events::{Event, EventType};
use crate::logging::LogLevel;
use crate::prover::input::InputParser;
use crate::system::{estimate_peak_gflops, measure_gflops, num_cores};
use crate::task::Task;
Expand Down Expand Up @@ -160,21 +158,12 @@ const CLI_USER_AGENT: &str = concat!("nexus-cli/", env!("CARGO_PKG_VERSION"));
static LAST_REPORT_BY_ADDRESS: OnceLock<Mutex<HashMap<String, Instant>>> = OnceLock::new();
/// Global wallet address for reporting; set once during session setup
static REPORT_WALLET_ADDRESS: OnceLock<String> = OnceLock::new();
/// Optional event sender for rewards notifications (TUI only); set during session setup
static REWARDS_EVENT_SENDER: OnceLock<tokio::sync::mpsc::Sender<crate::events::Event>> =
OnceLock::new();

/// Set the wallet address used for reporting proving activity
pub fn set_wallet_address_for_reporting(address: String) {
let _ = REPORT_WALLET_ADDRESS.set(address);
}

/// Set the event sender for rewards notifications (called from runtime when TUI is used).
/// When reportProving returns rewards_processed, an event is sent to display the notification.
pub fn set_rewards_event_sender(sender: tokio::sync::mpsc::Sender<crate::events::Event>) {
let _ = REWARDS_EVENT_SENDER.set(sender);
}

/// Report proving activity to our Cloud Function at most once per hour per wallet address.
/// When the response contains `{"result":{"status":"ok","message":"rewards_processed"}}`,
/// sends a rewards event to the TUI for display (if event sender is configured).
Expand Down Expand Up @@ -219,34 +208,7 @@ pub async fn report_proving_if_needed() {
.send()
.await;

// Parse response for rewards_processed; send TUI notification if present
if let Ok(resp) = response {
if let Ok(text) = resp.text().await {
if let Ok(json) = serde_json::from_str::<Value>(&text) {
if json
.get("result")
.and_then(|r| r.get("status"))
.and_then(|s| s.as_str())
== Some("ok")
&& json
.get("result")
.and_then(|r| r.get("message"))
.and_then(|m| m.as_str())
== Some("rewards_processed")
{
if let Some(sender) = REWARDS_EVENT_SENDER.get() {
let _ = sender
.send(Event::rewards_with_level(
crate::consts::cli_consts::REWARDS_PROCESSED_MESSAGE.to_string(),
EventType::Success,
LogLevel::Info,
))
.await;
}
}
}
}
}
drop(response);
}

/// Track analytics for getting a task from orchestrator (non-blocking)
Expand Down
4 changes: 0 additions & 4 deletions clients/cli/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ pub mod cli_consts {
/// The maximum number of events to keep in the activity logs.
pub const MAX_ACTIVITY_LOGS: usize = 100;

/// Message shown in header and activity log when reportProving returns rewards_processed
pub const REWARDS_PROCESSED_MESSAGE: &str =
"You've hit a cache of points! Claim at quest.nexus.xyz.";

/// Maximum number of event buffer size for worker threads
pub const EVENT_QUEUE_SIZE: usize = 100;

Expand Down
6 changes: 0 additions & 6 deletions clients/cli/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ pub enum Worker {
Prover(usize),
/// Worker that submits proofs to the orchestrator.
ProofSubmitter,
/// System-level notifications (e.g., rewards processed from reportProving).
Rewards,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, strum::Display)]
Expand Down Expand Up @@ -109,10 +107,6 @@ impl Event {
Self::new(Worker::Prover(thread_id), msg, event_type, log_level)
}

pub fn rewards_with_level(msg: String, event_type: EventType, log_level: LogLevel) -> Self {
Self::new(Worker::Rewards, msg, event_type, log_level)
}

pub fn should_display(&self) -> bool {
// Always show success events and info level events
if self.event_type == EventType::Success || self.log_level >= LogLevel::Info {
Expand Down
18 changes: 13 additions & 5 deletions clients/cli/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ pub fn is_valid_eth_address(address: &str) -> bool {
}

/// Validates EIP-55 checksum for Ethereum addresses.
///
///
/// EIP-55 defines a checksum encoding for Ethereum addresses that uses mixed case
/// to encode the address hash. This allows for error detection when typing addresses.
///
///
/// Algorithm:
/// 1. Convert address to lowercase
/// 2. Compute Keccak256 hash of the lowercase address
Expand All @@ -39,8 +39,12 @@ fn validate_eip55_checksum(address: &str) -> bool {
let hex = &address[2..];

// If the address is all lower or all upper, it's valid per EIP-55
if hex.chars().all(|c| !c.is_ascii_alphabetic() || c.is_ascii_lowercase())
|| hex.chars().all(|c| !c.is_ascii_alphabetic() || c.is_ascii_uppercase())
if hex
.chars()
.all(|c| !c.is_ascii_alphabetic() || c.is_ascii_lowercase())
|| hex
.chars()
.all(|c| !c.is_ascii_alphabetic() || c.is_ascii_uppercase())
{
return true;
}
Expand All @@ -59,7 +63,11 @@ fn validate_eip55_checksum(address: &str) -> bool {

// Determine the corresponding nibble from the hash
let hash_byte = hash[i / 2];
let nibble = if i % 2 == 0 { hash_byte >> 4 } else { hash_byte & 0x0F };
let nibble = if i % 2 == 0 {
hash_byte >> 4
} else {
hash_byte & 0x0F
};

if nibble >= 8 {
if !ch.is_ascii_uppercase() {
Expand Down
10 changes: 1 addition & 9 deletions clients/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,6 @@ enum Command {
/// Override max difficulty to request. Auto-promotion occurs when tasks complete in < 7 min
#[arg(long = "max-difficulty", value_name = "DIFFICULTY")]
max_difficulty: Option<String>,

/// [Debug] Show the rewards notification on startup for testing
#[arg(long = "show-mock-notification", hide = true, action = ArgAction::SetTrue)]
show_mock_notification: bool,
},
/// Register a new user
RegisterUser {
Expand Down Expand Up @@ -181,7 +177,6 @@ async fn main() -> Result<(), Box<dyn Error>> {
with_background,
max_tasks,
max_difficulty,
show_mock_notification,
} => {
// If a custom orchestrator URL is provided, create a custom environment
let final_environment = if let Some(url) = orchestrator_url {
Expand All @@ -201,7 +196,6 @@ async fn main() -> Result<(), Box<dyn Error>> {
with_background,
max_tasks,
max_difficulty,
show_mock_notification,
)
.await
}
Expand Down Expand Up @@ -247,7 +241,6 @@ async fn main() -> Result<(), Box<dyn Error>> {
/// * `check_mem` - Whether to check risky memory usage.
/// * `with_background` - Whether to use the alternate TUI background color.
/// * `max_tasks` - Optional maximum number of tasks to prove.
/// * `show_mock_notification` - [Debug] Show rewards overlay on startup for testing.
#[allow(clippy::too_many_arguments)]
async fn start(
node_id: Option<u64>,
Expand All @@ -259,7 +252,6 @@ async fn start(
with_background: bool,
max_tasks: Option<u32>,
max_difficulty: Option<String>,
show_mock_notification: bool,
) -> Result<(), Box<dyn Error>> {
// 1. Version checking (will internally perform country detection without race)
validate_version_requirements().await?;
Expand Down Expand Up @@ -302,7 +294,7 @@ async fn start(
if headless {
run_headless_mode(session).await
} else {
run_tui_mode(session, with_background, show_mock_notification).await
run_tui_mode(session, with_background).await
}
}

Expand Down
2 changes: 1 addition & 1 deletion clients/cli/src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ mod tests {
let dir = tempdir().unwrap();
let config_path = dir.path().join("config.json");

let wallet_address = "0xABCDEFabcdef1234567890123456789012345678";
let wallet_address = "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed";
let user_id = "existing-user-id";

// Write a pre-existing config with matching wallet and user_id
Expand Down
4 changes: 0 additions & 4 deletions clients/cli/src/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Simplified runtime for coordinating authenticated workers

use crate::analytics::set_rewards_event_sender;
use crate::environment::Environment;
use crate::events::Event;
use crate::orchestrator::OrchestratorClient;
Expand Down Expand Up @@ -33,9 +32,6 @@ pub async fn start_authenticated_worker(
let (event_sender, event_receiver) =
mpsc::channel::<Event>(crate::consts::cli_consts::EVENT_QUEUE_SIZE);

// Allow analytics to send rewards_processed notifications to the TUI
set_rewards_event_sender(event_sender.clone());

// Create a separate shutdown sender for max tasks completion
let (shutdown_sender, _) = broadcast::channel(1);

Expand Down
3 changes: 0 additions & 3 deletions clients/cli/src/session/tui_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@ use std::{error::Error, io};
/// # Arguments
/// * `session` - Session data from setup
/// * `with_background` - Whether to enable background colors
/// * `show_mock_notification` - [Debug] Show rewards overlay on startup for testing
///
/// # Returns
/// * `Ok(())` - TUI mode completed successfully
/// * `Err` - TUI mode failed
pub async fn run_tui_mode(
session: SessionData,
with_background: bool,
show_mock_notification: bool,
) -> Result<(), Box<dyn Error>> {
// Print session start message
print_session_starting("TUI", session.node_id);
Expand Down Expand Up @@ -67,7 +65,6 @@ pub async fn run_tui_mode(
session.num_workers,
version_update_available,
latest_version,
show_mock_notification,
);

let app = ui::App::new(
Expand Down
11 changes: 0 additions & 11 deletions clients/cli/src/ui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ pub struct UIConfig {
pub num_threads: usize,
pub update_available: bool,
pub latest_version: Option<String>,
/// [Debug] Show rewards overlay on startup for testing
pub show_mock_notification: bool,
}

impl UIConfig {
Expand All @@ -29,14 +27,12 @@ impl UIConfig {
num_threads: usize,
update_available: bool,
latest_version: Option<String>,
show_mock_notification: bool,
) -> Self {
Self {
with_background_color,
num_threads,
update_available,
latest_version,
show_mock_notification,
}
}
}
Expand Down Expand Up @@ -88,9 +84,6 @@ pub struct App {

/// Latest version available, if any.
latest_version: Option<String>,

/// [Debug] Show rewards overlay on startup for testing
show_mock_notification: bool,
}

impl App {
Expand All @@ -115,7 +108,6 @@ impl App {
num_threads: ui_config.num_threads,
version_update_available: ui_config.update_available,
latest_version: ui_config.latest_version,
show_mock_notification: ui_config.show_mock_notification,
}
}

Expand All @@ -128,7 +120,6 @@ impl App {
self.num_threads,
self.version_update_available,
self.latest_version.clone(),
self.show_mock_notification,
);
let state = DashboardState::new(
node_id,
Expand Down Expand Up @@ -181,7 +172,6 @@ pub async fn run<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> std::i
app.num_threads,
app.version_update_available,
app.latest_version.clone(),
app.show_mock_notification,
);
app.current_screen = Screen::Dashboard(Box::new(DashboardState::new(
app.node_id,
Expand Down Expand Up @@ -217,7 +207,6 @@ pub async fn run<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> std::i
app.num_threads,
app.version_update_available,
app.latest_version.clone(),
app.show_mock_notification,
);
app.current_screen = Screen::Dashboard(Box::new(DashboardState::new(
app.node_id,
Expand Down
9 changes: 2 additions & 7 deletions clients/cli/src/ui/dashboard/components/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,9 @@ pub fn render_header(f: &mut Frame, area: ratatui::layout::Rect, state: &Dashboa
.constraints([Constraint::Length(2), Constraint::Length(2)])
.split(area);

// Title section: rewards notification takes priority, then version update, else default
// Title section: version update takes priority, else default
let version = env!("CARGO_PKG_VERSION");
let (title_text, title_color) = if state.show_rewards_overlay {
(
crate::consts::cli_consts::REWARDS_PROCESSED_MESSAGE.to_string(),
Color::Rgb(255, 193, 7), // Amber/gold
)
} else if state.update_available {
let (title_text, title_color) = if state.update_available {
let text = if let Some(latest) = &state.latest_version {
format!("NEXUS PROVER v{} -> {} UPDATE AVAILABLE", version, latest)
} else {
Expand Down
3 changes: 0 additions & 3 deletions clients/cli/src/ui/dashboard/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ pub struct DashboardState {
pub step2_start_time: Option<Instant>,
/// Track the start time and original wait duration for current waiting period
pub waiting_start_info: Option<(Instant, u64)>, // (start_time, original_wait_secs)
/// Whether to show the rewards_processed congratulations overlay (dismissed on next proof submission)
pub show_rewards_overlay: bool,
}

impl DashboardState {
Expand Down Expand Up @@ -105,7 +103,6 @@ impl DashboardState {
current_prover_state: ProverState::Waiting,
step2_start_time: None,
waiting_start_info: None,
show_rewards_overlay: ui_config.show_mock_notification,
}
}
// Getter methods for private fields
Expand Down
7 changes: 0 additions & 7 deletions clients/cli/src/ui/dashboard/updaters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ impl DashboardState {
Worker::TaskFetcher => self.handle_task_fetcher_event(event),
Worker::Prover(_) => self.handle_prover_event(event),
Worker::ProofSubmitter => self.handle_proof_submitter_event(event),
Worker::Rewards => self.handle_rewards_event(event),
}

// Handle state changes regardless of worker
Expand Down Expand Up @@ -141,12 +140,6 @@ impl DashboardState {
}
}

/// Handle Rewards events (rewards_processed from reportProving).
/// Worker::Rewards is only sent when reportProving returns rewards_processed.
fn handle_rewards_event(&mut self, _event: &WorkerEvent) {
self.show_rewards_overlay = true;
}

/// Update task fetch countdown based on current waiting state
fn update_task_fetch_countdown(&mut self) {
if let Some((start_time, original_secs)) = &self.waiting_start_info {
Expand Down
1 change: 0 additions & 1 deletion clients/cli/src/ui/dashboard/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ pub fn get_worker_color(worker: &Worker) -> Color {
Worker::TaskFetcher => Color::Cyan,
Worker::Prover(_) => Color::Yellow,
Worker::ProofSubmitter => Color::Green,
Worker::Rewards => Color::LightYellow,
}
}

Expand Down
Loading