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
109 changes: 74 additions & 35 deletions mutant-daemon/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{collections::HashMap, net::SocketAddr, sync::Arc};
use xdg::BaseDirectories;

use mutant_lib::{config::NetworkChoice, MutAnt};
use tokio::sync::RwLock;
use tokio::sync::{RwLock, OnceCell};
use warp::Filter;

use crate::error::Error;
Expand All @@ -13,6 +13,46 @@ use crate::wallet;
use std::path::PathBuf;
use tokio::signal;

// Thread-safe singleton to track public-only mode
pub static PUBLIC_ONLY_MODE: OnceCell<bool> = OnceCell::const_new();

/// Helper function to initialize MutAnt based on network choice and private key
async fn init_mutant(network_choice: NetworkChoice, private_key: Option<String>) -> Result<(MutAnt, bool), Error> {
let is_public_only = private_key.is_none();

let mutant = match (network_choice, private_key) {
// Full access with private key
(NetworkChoice::Devnet, Some(_)) => {
log::info!("Running in local mode");
MutAnt::init_local().await
}
(NetworkChoice::Alphanet, Some(key)) => {
log::info!("Running in alphanet mode");
MutAnt::init_alphanet(&key).await
}
(NetworkChoice::Mainnet, Some(key)) => {
log::info!("Running in mainnet mode");
MutAnt::init(&key).await
}

// Public-only mode (no private key)
(NetworkChoice::Devnet, None) => {
log::info!("Running in local public-only mode");
MutAnt::init_public_local().await
}
(NetworkChoice::Alphanet, None) => {
log::info!("Running in alphanet public-only mode");
MutAnt::init_public_alphanet().await
}
(NetworkChoice::Mainnet, None) => {
log::info!("Running in mainnet public-only mode");
MutAnt::init_public().await
}
}.map_err(Error::MutAnt)?;

Ok((mutant, is_public_only))
}

#[derive(serde::Deserialize, serde::Serialize)]
struct NetworkConfig {
public_key: String,
Expand Down Expand Up @@ -129,50 +169,49 @@ pub async fn run(options: AppOptions) -> Result<(), Error> {

let mut config = Config::load()?;

let private_key_for_mutant: String;

match config.get_private_key(network_choice)? {
// Try to get a private key from config
let private_key = match config.get_private_key(network_choice)? {
Some((key_from_file, pk_hex)) => {
log::info!(
"Loaded private key from file for network {:?}: {}",
network_choice,
pk_hex
);
private_key_for_mutant = key_from_file;
Some(key_from_file)
}
None => {
log::info!("No private key found in config or file, attempting to scan wallets for network {:?}", network_choice);
let (selected_private_key, pk_hex) = wallet::scan_and_select_wallet().await?;

config.set_public_key(network_choice, pk_hex.clone());
config.save()?;
log::info!(
"Saved newly selected public key {} to config for network {:?}",
pk_hex,
network_choice
);
private_key_for_mutant = selected_private_key;
}
}

let mutant = Arc::new(
match network_choice {
NetworkChoice::Devnet => {
log::info!("Running in local mode");
MutAnt::init_local().await
}
NetworkChoice::Alphanet => {
log::info!("Running in alphanet mode");
MutAnt::init_alphanet(&private_key_for_mutant).await
}
NetworkChoice::Mainnet => {
log::info!("Running in mainnet mode");
MutAnt::init(&private_key_for_mutant).await
// Try to scan for wallets
match wallet::scan_and_select_wallet().await {
Ok((selected_private_key, pk_hex)) => {
// Save the selected wallet for future use
config.set_public_key(network_choice, pk_hex.clone());
config.save()?;
log::info!(
"Saved newly selected public key {} to config for network {:?}",
pk_hex,
network_choice
);
Some(selected_private_key)
}
Err(e) => {
// No wallet found, initialize in public-only mode
log::warn!("No wallet found: {}. Initializing in public-only mode.", e);
log::info!("Only public downloads (mutant get -p) will be available.");
None
}
}
}
.map_err(Error::MutAnt)?,
);
log::info!("MutAnt initialized successfully.");
};

// Initialize MutAnt with the appropriate mode
let (mutant, is_public_only) = init_mutant(network_choice, private_key).await?;
let mutant = Arc::new(mutant);

// Set the public-only mode flag
PUBLIC_ONLY_MODE.set(is_public_only).expect("PUBLIC_ONLY_MODE should only be set once");

log::info!("MutAnt initialized successfully{}",
if is_public_only { " in public-only mode" } else { "" });

// Initialize Task Management
let tasks: TaskMap = Arc::new(RwLock::new(HashMap::new()));
Expand Down
22 changes: 21 additions & 1 deletion mutant-daemon/src/handlers/data_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use tokio::fs;
use uuid::Uuid;

use crate::error::Error as DaemonError;
use super::{TaskEntry, TaskMap, ActiveKeysMap, try_register_key, release_key};
use super::{TaskEntry, TaskMap, ActiveKeysMap, try_register_key, release_key, is_public_only_mode, PUBLIC_ONLY_ERROR_MSG};
use mutant_lib::storage::ScratchpadAddress;
use mutant_lib::MutAnt;
use mutant_protocol::{
Expand All @@ -22,6 +22,16 @@ pub(crate) async fn handle_put(
active_keys: ActiveKeysMap,
original_request_str: &str,
) -> Result<(), DaemonError> {
// Check if we're in public-only mode
if is_public_only_mode() {
return update_tx
.send(Response::Error(ErrorResponse {
error: PUBLIC_ONLY_ERROR_MSG.to_string(),
original_request: Some(original_request_str.to_string()),
}))
.map_err(|e| DaemonError::Internal(format!("Update channel send error: {}", e)));
}

let task_id = Uuid::new_v4();
let user_key = req.user_key.clone();
let source_path = req.source_path.clone(); // Keep path for logging
Expand Down Expand Up @@ -428,6 +438,16 @@ pub(crate) async fn handle_rm(
active_keys: ActiveKeysMap,
original_request_str: &str,
) -> Result<(), DaemonError> {
// Check if we're in public-only mode
if is_public_only_mode() {
return update_tx
.send(Response::Error(ErrorResponse {
error: PUBLIC_ONLY_ERROR_MSG.to_string(),
original_request: Some(original_request_str.to_string()),
}))
.map_err(|e| DaemonError::Internal(format!("Update channel send error: {}", e)));
}

let task_id = Uuid::new_v4();
let user_key = req.user_key.clone();
log::info!("Starting RM task: user_key={}", user_key);
Expand Down
23 changes: 22 additions & 1 deletion mutant-daemon/src/handlers/import_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ use std::sync::Arc;
use tokio::fs;

use crate::error::Error as DaemonError;
use super::{is_public_only_mode, PUBLIC_ONLY_ERROR_MSG};
use mutant_lib::storage::PadInfo;
use mutant_lib::MutAnt;
use mutant_protocol::{
ExportRequest, ExportResponse, ExportResult, ImportRequest, ImportResponse, ImportResult,
ErrorResponse, ExportRequest, ExportResponse, ExportResult, ImportRequest, ImportResponse, ImportResult,
Response,
};

Expand All @@ -18,6 +19,16 @@ pub(crate) async fn handle_import(
) -> Result<(), DaemonError> {
log::debug!("Handling Import request");

// Check if we're in public-only mode
if is_public_only_mode() {
return update_tx
.send(Response::Error(ErrorResponse {
error: PUBLIC_ONLY_ERROR_MSG.to_string(),
original_request: Some(serde_json::to_string(&req).unwrap_or_default()),
}))
.map_err(|e| DaemonError::Internal(format!("Update channel send error: {}", e)));
}

let file_path = req.file_path.clone();
let pads_hex = fs::read(&file_path)
.await
Expand Down Expand Up @@ -50,6 +61,16 @@ pub(crate) async fn handle_export(
) -> Result<(), DaemonError> {
log::debug!("Handling Export request");

// Check if we're in public-only mode
if is_public_only_mode() {
return update_tx
.send(Response::Error(ErrorResponse {
error: PUBLIC_ONLY_ERROR_MSG.to_string(),
original_request: Some(serde_json::to_string(&req).unwrap_or_default()),
}))
.map_err(|e| DaemonError::Internal(format!("Update channel send error: {}", e)));
}

let destination_path = req.destination_path.clone();

let pads_hex = mutant.export_raw_pads_private_key().await?;
Expand Down
9 changes: 9 additions & 0 deletions mutant-daemon/src/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@ mod key_management;
pub use websocket::handle_ws;
pub use task_management::{TaskEntry, TaskMap};
pub use key_management::{ActiveKeysMap, try_register_key, release_key};

/// Check if the daemon is running in public-only mode
pub fn is_public_only_mode() -> bool {
// If the OnceCell hasn't been initialized yet, we're not in public-only mode
crate::app::PUBLIC_ONLY_MODE.get().copied().unwrap_or(false)
}

/// Error message for operations that require a wallet when in public-only mode
pub const PUBLIC_ONLY_ERROR_MSG: &str = "This operation requires a wallet, but the daemon is running in public-only mode. To enable full functionality, please set up an Autonomi wallet using 'ant wallet import' or 'ant wallet create' and restart the daemon.";
37 changes: 35 additions & 2 deletions mutant-daemon/src/handlers/system_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use std::sync::Arc;
use uuid::Uuid;

use crate::error::Error as DaemonError;
use super::{TaskMap, TaskEntry};
use super::{TaskMap, TaskEntry, is_public_only_mode, PUBLIC_ONLY_ERROR_MSG};
use mutant_lib::MutAnt;
use mutant_protocol::{
HealthCheckCallback, HealthCheckEvent, HealthCheckRequest, PurgeCallback, PurgeEvent,
ErrorResponse, HealthCheckCallback, HealthCheckEvent, HealthCheckRequest, PurgeCallback, PurgeEvent,
PurgeRequest, Response, SyncCallback, SyncEvent, SyncRequest, Task, TaskCreatedResponse,
TaskProgress, TaskResult, TaskResultResponse, TaskResultType, TaskStatus, TaskType,
TaskUpdateResponse,
Expand All @@ -19,6 +19,16 @@ pub(crate) async fn handle_sync(
mutant: Arc<MutAnt>,
tasks: TaskMap,
) -> Result<(), DaemonError> {
// Check if we're in public-only mode
if is_public_only_mode() {
return update_tx
.send(Response::Error(ErrorResponse {
error: PUBLIC_ONLY_ERROR_MSG.to_string(),
original_request: Some(serde_json::to_string(&SyncRequest { push_force: req.push_force }).unwrap_or_default()),
}))
.map_err(|e| DaemonError::Internal(format!("Update channel send error: {}", e)));
}

let task_id = Uuid::new_v4();

let task = Task {
Expand Down Expand Up @@ -159,6 +169,16 @@ pub(crate) async fn handle_purge(
mutant: Arc<MutAnt>,
tasks: TaskMap,
) -> Result<(), DaemonError> {
// Check if we're in public-only mode
if is_public_only_mode() {
return update_tx
.send(Response::Error(ErrorResponse {
error: PUBLIC_ONLY_ERROR_MSG.to_string(),
original_request: Some(serde_json::to_string(&PurgeRequest { aggressive: req.aggressive }).unwrap_or_default()),
}))
.map_err(|e| DaemonError::Internal(format!("Update channel send error: {}", e)));
}

let task_id = Uuid::new_v4();

let task = Task {
Expand Down Expand Up @@ -300,6 +320,19 @@ pub(crate) async fn handle_health_check(
mutant: Arc<MutAnt>,
tasks: TaskMap,
) -> Result<(), DaemonError> {
// Check if we're in public-only mode
if is_public_only_mode() {
return update_tx
.send(Response::Error(ErrorResponse {
error: PUBLIC_ONLY_ERROR_MSG.to_string(),
original_request: Some(serde_json::to_string(&HealthCheckRequest {
key_name: req.key_name.clone(),
recycle: req.recycle
}).unwrap_or_default()),
}))
.map_err(|e| DaemonError::Internal(format!("Update channel send error: {}", e)));
}

let task_id = Uuid::new_v4();

let task = Task {
Expand Down
Loading