Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
borngraced committed Jan 9, 2025
1 parent 07416d1 commit dc16267
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 46 deletions.
114 changes: 71 additions & 43 deletions mm2src/mm2_main/src/lp_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use mm2_err_handle::prelude::*;
use serde::de::DeserializeOwned;
use serde_json::{self as json, Value as Json};

use self::mnemonics_storage::update_seed_storage_password;

cfg_wasm32! {
use crate::lp_wallet::mnemonics_wasm_db::{WalletsDb, WalletsDBError};
use mm2_core::mm_ctx::from_ctx;
Expand Down Expand Up @@ -415,38 +417,42 @@ pub struct GetMnemonicResponse {

#[derive(Debug, Display, Serialize, SerializeErrorType)]
#[serde(tag = "error_type", content = "error_data")]
pub enum GetMnemonicError {
pub enum WalletStorageRpcError {
#[display(fmt = "Invalid request error: {}", _0)]
InvalidRequest(String),
#[display(fmt = "Wallets storage error: {}", _0)]
WalletsStorageError(String),
#[display(fmt = "Internal error: {}", _0)]
Internal(String),
#[display(fmt = "Invalid password error: {}", _0)]
InvalidPassword(String),
}

impl HttpStatusCode for GetMnemonicError {
impl HttpStatusCode for WalletStorageRpcError {
fn status_code(&self) -> StatusCode {
match self {
GetMnemonicError::InvalidRequest(_) => StatusCode::BAD_REQUEST,
GetMnemonicError::WalletsStorageError(_) | GetMnemonicError::Internal(_) => {
WalletStorageRpcError::InvalidRequest(_) | WalletStorageRpcError::InvalidPassword(_) => {
StatusCode::BAD_REQUEST
},
WalletStorageRpcError::WalletsStorageError(_) | WalletStorageRpcError::Internal(_) => {
StatusCode::INTERNAL_SERVER_ERROR
},
}
}
}

#[cfg(not(target_arch = "wasm32"))]
impl From<WalletsStorageError> for GetMnemonicError {
fn from(e: WalletsStorageError) -> Self { GetMnemonicError::WalletsStorageError(e.to_string()) }
impl From<WalletsStorageError> for WalletStorageRpcError {
fn from(e: WalletsStorageError) -> Self { WalletStorageRpcError::WalletsStorageError(e.to_string()) }
}

#[cfg(target_arch = "wasm32")]
impl From<WalletsDBError> for GetMnemonicError {
fn from(e: WalletsDBError) -> Self { GetMnemonicError::WalletsStorageError(e.to_string()) }
impl From<WalletsDBError> for WalletStorageRpcError {
fn from(e: WalletsDBError) -> Self { WalletStorageRpcError::WalletsStorageError(e.to_string()) }
}

impl From<ReadPassphraseError> for GetMnemonicError {
fn from(e: ReadPassphraseError) -> Self { GetMnemonicError::WalletsStorageError(e.to_string()) }
impl From<ReadPassphraseError> for WalletStorageRpcError {
fn from(e: ReadPassphraseError) -> Self { WalletStorageRpcError::WalletsStorageError(e.to_string()) }
}

/// Retrieves the wallet mnemonic in the requested format.
Expand All @@ -456,7 +462,7 @@ impl From<ReadPassphraseError> for GetMnemonicError {
/// A `Result` type containing:
///
/// * [`Ok`]([`GetMnemonicResponse`]) - The wallet mnemonic in the requested format.
/// * [`MmError`]<[`GetMnemonicError>`]> - Returns specific [`GetMnemonicError`] variants for different failure scenarios.
/// * [`MmError`]<[`WalletStorageRpcError>`]> - Returns specific [`GetMnemonicError`] variants for different failure scenarios.
///
/// # Errors
///
Expand All @@ -480,20 +486,23 @@ impl From<ReadPassphraseError> for GetMnemonicError {
/// Err(e) => println!("Error: {:?}", e),
/// }
/// ```
pub async fn get_mnemonic_rpc(ctx: MmArc, req: GetMnemonicRequest) -> MmResult<GetMnemonicResponse, GetMnemonicError> {
pub async fn get_mnemonic_rpc(
ctx: MmArc,
req: GetMnemonicRequest,
) -> MmResult<GetMnemonicResponse, WalletStorageRpcError> {
match req.mnemonic_format {
MnemonicFormat::Encrypted => {
let encrypted_mnemonic = read_encrypted_passphrase_if_available(&ctx)
.await?
.ok_or_else(|| GetMnemonicError::InvalidRequest("Wallet mnemonic file not found".to_string()))?;
.ok_or_else(|| WalletStorageRpcError::InvalidRequest("Wallet mnemonic file not found".to_string()))?;
Ok(GetMnemonicResponse {
mnemonic: encrypted_mnemonic.into(),
})
},
MnemonicFormat::PlainText(wallet_password) => {
let plaintext_mnemonic = read_and_decrypt_passphrase_if_available(&ctx, &wallet_password)
.await?
.ok_or_else(|| GetMnemonicError::InvalidRequest("Wallet mnemonic file not found".to_string()))?;
.ok_or_else(|| WalletStorageRpcError::InvalidRequest("Wallet mnemonic file not found".to_string()))?;
Ok(GetMnemonicResponse {
mnemonic: plaintext_mnemonic.into(),
})
Expand All @@ -508,40 +517,13 @@ pub struct GetWalletNamesResponse {
activated_wallet: Option<String>,
}

#[derive(Debug, Display, Serialize, SerializeErrorType)]
#[serde(tag = "error_type", content = "error_data")]
pub enum GetWalletsError {
#[display(fmt = "Wallets storage error: {}", _0)]
WalletsStorageError(String),
#[display(fmt = "Internal error: {}", _0)]
Internal(String),
}

impl HttpStatusCode for GetWalletsError {
fn status_code(&self) -> StatusCode {
match self {
GetWalletsError::WalletsStorageError(_) | GetWalletsError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}

#[cfg(not(target_arch = "wasm32"))]
impl From<WalletsStorageError> for GetWalletsError {
fn from(e: WalletsStorageError) -> Self { GetWalletsError::WalletsStorageError(e.to_string()) }
}

#[cfg(target_arch = "wasm32")]
impl From<WalletsDBError> for GetWalletsError {
fn from(e: WalletsDBError) -> Self { GetWalletsError::WalletsStorageError(e.to_string()) }
}

/// Retrieves all created wallets and the currently activated wallet.
pub async fn get_wallet_names_rpc(ctx: MmArc, _req: Json) -> MmResult<GetWalletNamesResponse, GetWalletsError> {
pub async fn get_wallet_names_rpc(ctx: MmArc, _req: Json) -> MmResult<GetWalletNamesResponse, WalletStorageRpcError> {
// We want to return wallet names in the same order for both native and wasm32 targets.
let wallets = read_all_wallet_names(&ctx).await?.sorted().collect();
// Note: `ok_or` is used here on `Constructible<Option<String>>` to handle the case where the wallet name is not set.
// `wallet_name` can be `None` in the case of no-login mode.
let activated_wallet = ctx.wallet_name.get().ok_or(GetWalletsError::Internal(
let activated_wallet = ctx.wallet_name.get().ok_or(WalletStorageRpcError::Internal(
"`wallet_name` not initialized yet!".to_string(),
))?;

Expand All @@ -550,3 +532,49 @@ pub async fn get_wallet_names_rpc(ctx: MmArc, _req: Json) -> MmResult<GetWalletN
activated_wallet: activated_wallet.clone(),
})
}

/// `SeedPasswordUpdateRequest` represents a request to update
/// the password for the seed storage.
/// It includes the current password and the new password to be set.
#[derive(Debug, Deserialize)]
pub struct SeedPasswordUpdateRequest {
/// The current password for the seed storage.
pub current_password: String,
/// The new password to replace the current password.
pub new_password: String,
}

/// `SeedPasswordUpdateResponse` represents the result of a
/// password update request.
/// It contains a boolean indicating whether the operation was successful.
#[derive(Serialize)]
pub struct SeedPasswordUpdateResponse {
/// `true` if the password update was successful, `false` otherwise.
result: bool,
}

/// RPC function to handle a request for updating the seed storage password.
///
/// # Arguments
/// - `ctx`: The shared context (`MmArc`) for the application.
/// - `req`: The `SeedPasswordUpdateRequest` containing the current and new passwords.
///
/// # Example
/// ```ignore
/// let request = SeedPasswordUpdateRequest {
/// current_password: "old_password".to_string(),
/// new_password: "new_password".to_string(),
/// };
/// let response = update_seed_storage_password_rpc(ctx, request).await?;
/// assert!(response.result);
/// ```
pub async fn update_seed_storage_password_rpc(
ctx: MmArc,
req: SeedPasswordUpdateRequest,
) -> MmResult<SeedPasswordUpdateResponse, WalletStorageRpcError> {
Ok(
update_seed_storage_password(&ctx, &req.current_password, &req.new_password)
.await
.map(|_| SeedPasswordUpdateResponse { result: true })?,
)
}
42 changes: 40 additions & 2 deletions mm2src/mm2_main/src/lp_wallet/mnemonics_storage.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
use crypto::EncryptedData;
use crypto::{encrypt_mnemonic, EncryptedData, MnemonicError};
use enum_derives::EnumFromStringify;
use mm2_core::mm_ctx::MmArc;
use mm2_err_handle::prelude::*;
use mm2_io::fs::{ensure_file_is_writable, list_files_by_extension};

use super::{read_and_decrypt_passphrase_if_available, ReadPassphraseError};

type WalletsStorageResult<T> = Result<T, MmError<WalletsStorageError>>;

#[derive(Debug, Deserialize, Display, Serialize)]
#[derive(Debug, Deserialize, Display, Serialize, EnumFromStringify)]
pub enum WalletsStorageError {
#[display(fmt = "Error writing to file: {}", _0)]
FsWriteError(String),
#[display(fmt = "Error reading from file: {}", _0)]
FsReadError(String),
#[display(fmt = "Internal error: {}", _0)]
Internal(String),
#[display(fmt = "Mnemonic error: {}", _0)]
#[from_stringify("MnemonicError")]
MnemonicError(String),
#[display(fmt = "Read Passphrase error: {}", _0)]
#[from_stringify("ReadPassphraseError")]
ReadPassphraseError(String),
}

/// Saves the passphrase to a file associated with the given wallet name.
Expand Down Expand Up @@ -69,3 +78,32 @@ pub(super) async fn read_all_wallet_names(ctx: &MmArc) -> WalletsStorageResult<i
.mm_err(|e| WalletsStorageError::FsReadError(format!("Error reading wallets directory: {}", e)))?;
Ok(wallet_names)
}

/// Update the password to a file associated with the given wallet name.
pub async fn update_seed_storage_password(
ctx: &MmArc,
current_password: &str,
new_password: &str,
) -> WalletsStorageResult<()> {
let wallet_name = ctx
.wallet_name
.get()
.ok_or(WalletsStorageError::Internal(
"`wallet_name` not initialized yet!".to_string(),
))?
.clone()
.ok_or_else(|| WalletsStorageError::Internal("`wallet_name` cannot be None!".to_string()))?;
// read mnemonic for a wallet_name using current user's password.
let decrypted = read_and_decrypt_passphrase_if_available(ctx, current_password).await?;
if let Some(mnemonic) = decrypted {
// encrypt mnemonic with new passphrase.
let encrypted_data = encrypt_mnemonic(&mnemonic, new_password)?;
// save new encrypted mnemonic data with new password
save_encrypted_passphrase(ctx, &wallet_name, &encrypted_data).await?;
return Ok(());
};

MmError::err(WalletsStorageError::Internal(format!(
"{wallet_name}: wallet mnemonic file not found"
)))
}
3 changes: 2 additions & 1 deletion mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::lp_stats::{add_node_to_version_stat, remove_node_from_version_stat, s
stop_version_stat_collection, update_version_stat_collection};
use crate::lp_swap::swap_v2_rpcs::{active_swaps_rpc, my_recent_swaps_rpc, my_swap_status_rpc};
use crate::lp_swap::{get_locked_amount_rpc, max_maker_vol, recreate_swap_data, trade_preimage_rpc};
use crate::lp_wallet::{get_mnemonic_rpc, get_wallet_names_rpc};
use crate::lp_wallet::{get_mnemonic_rpc, get_wallet_names_rpc, update_seed_storage_password_rpc};
use crate::rpc::lp_commands::db_id::get_shared_db_id;
use crate::rpc::lp_commands::one_inch::rpcs::{one_inch_v6_0_classic_swap_contract_rpc,
one_inch_v6_0_classic_swap_create_rpc,
Expand Down Expand Up @@ -217,6 +217,7 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult<Re
"trade_preimage" => handle_mmrpc(ctx, request, trade_preimage_rpc).await,
"trezor_connection_status" => handle_mmrpc(ctx, request, trezor_connection_status).await,
"update_nft" => handle_mmrpc(ctx, request, update_nft).await,
"update_seed_storage_password" => handle_mmrpc(ctx, request, update_seed_storage_password_rpc).await,
"update_version_stat_collection" => handle_mmrpc(ctx, request, update_version_stat_collection).await,
"verify_message" => handle_mmrpc(ctx, request, verify_message).await,
"withdraw" => handle_mmrpc(ctx, request, withdraw).await,
Expand Down

0 comments on commit dc16267

Please sign in to comment.