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: wallet unlock rendering #145

Merged
merged 3 commits into from
Jan 7, 2025
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
116 changes: 116 additions & 0 deletions src/ui/identities/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
use std::sync::{Arc, RwLock};

use dash_sdk::{
dpp::{
data_contract::accessors::v0::DataContractV0Getters,
identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0,
},
platform::IdentityPublicKey,
};

use crate::{
context::AppContext,
model::{
qualified_identity::{
encrypted_key_storage::PrivateKeyData, PrivateKeyTarget, QualifiedIdentity,
},
wallet::Wallet,
},
};

pub mod add_existing_identity_screen;
pub mod add_new_identity_screen;
mod funding_common;
Expand All @@ -7,3 +27,99 @@ pub mod register_dpns_name_screen;
pub mod top_up_identity_screen;
pub mod transfers;
pub mod withdraw_from_identity_screen;

/// Retrieves the appropriate wallet (if any) associated with the given identity.
///
/// # Description
///
/// This function tries to determine which wallet should be used, either via:
///
/// - The DPNS-based approach (if [`AppContext`] is provided), which looks up
/// the `preorder` document type in the DPNS contract and retrieves the
/// document-signing key from the given [`QualifiedIdentity`].
/// - The fallback approach (if `app_context` is `None`), which relies on a
/// directly provided key (`selected_key`).
///
/// # Parameters
///
/// - `qualified_identity`: A reference to the [`QualifiedIdentity`], which holds
/// the identity, keys, and associated wallets.
/// - `app_context`: Optional reference to the [`AppContext`] which contains the
/// DPNS contract. When present, DPNS logic is used to find the public key.
/// - `selected_key`: An optional reference to a chosen [`IdentityPublicKey`].
/// When `app_context` is not provided, this is required to get the wallet.
/// - `error_message`: A mutable optional string where any error message will
/// be written if the function fails to retrieve a wallet.
///
/// # Returns
///
/// Returns `Some(Arc<RwLock<Wallet>>)` if a matching wallet is found, or `None`
/// otherwise. If an error is encountered, an explanatory message is placed in
/// `error_message`.
///
/// # Errors
///
/// - If the DPNS document type can't be found or the identity is missing the
/// required DPNS signing key (when `app_context` is provided).
/// - If no `selected_key` is provided (when `app_context` is `None`).
/// - If the derived wallet derivation path is missing from the
/// [`QualifiedIdentity`].
pub fn get_selected_wallet(
qualified_identity: &QualifiedIdentity,
app_context: Option<&AppContext>, // Used for DPNS-based logic (the first scenario).
selected_key: Option<&IdentityPublicKey>, // Used for direct-key logic (the fallback scenario).
error_message: &mut Option<String>,
) -> Option<Arc<RwLock<Wallet>>> {
// If `app_context` is provided, use the DPNS-based approach.
let public_key = if let Some(context) = app_context {
let dpns_contract = &context.dpns_contract;

// Attempt to fetch the `preorder` document type from the DPNS contract.
let preorder_document_type = match dpns_contract.document_type_for_name("preorder") {
Ok(doc_type) => doc_type,
Err(e) => {
*error_message = Some(format!("DPNS preorder document type not found: {}", e));
return None;
}
};

// Attempt to retrieve the public key from the identity.
match qualified_identity.document_signing_key(&preorder_document_type) {
Some(key) => key,
None => {
*error_message = Some(
"Identity doesn't have an authentication key for signing document transitions"
.to_string(),
);
return None;
}
}
} else {
// Fallback: directly use the provided selected key.
match selected_key {
Some(key) => key,
None => {
*error_message = Some("No key provided when getting selected wallet".to_string());
return None;
}
}
};

// Once we have the public key (either from DPNS or directly), look up
// the matching private key data in `qualified_identity`.
let key_lookup = (PrivateKeyTarget::PrivateKeyOnMainIdentity, public_key.id());
if let Some((_, PrivateKeyData::AtWalletDerivationPath(wallet_derivation_path))) =
qualified_identity
.private_keys
.private_keys
.get(&key_lookup)
{
// If found, return the associated wallet (cloned to preserve Arc).
qualified_identity
.associated_wallets
.get(&wallet_derivation_path.wallet_seed_hash)
.cloned()
} else {
None
}
}
58 changes: 12 additions & 46 deletions src/ui/identities/register_dpns_name_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use crate::app::AppAction;
use crate::backend_task::identity::{IdentityTask, RegisterDpnsNameInput};
use crate::backend_task::BackendTask;
use crate::context::AppContext;
use crate::model::qualified_identity::encrypted_key_storage::PrivateKeyData;
use crate::model::qualified_identity::{PrivateKeyTarget, QualifiedIdentity};
use crate::model::qualified_identity::QualifiedIdentity;
use crate::model::wallet::Wallet;
use crate::ui::components::top_panel::add_top_panel;
use crate::ui::components::wallet_unlock::ScreenWithWalletUnlock;
Expand All @@ -21,6 +20,8 @@ use std::sync::Arc;
use std::sync::RwLock;
use std::time::{SystemTime, UNIX_EPOCH};

use super::get_selected_wallet;

#[derive(PartialEq)]
pub enum RegisterDpnsNameStatus {
NotStarted,
Expand Down Expand Up @@ -78,7 +79,7 @@ impl RegisterDpnsNameScreen {

let mut error_message: Option<String> = None;
let selected_wallet = if let Some(ref identity) = selected_qualified_identity {
get_selected_wallet(&identity.0, app_context, &mut error_message)
get_selected_wallet(&identity.0, Some(app_context), None, &mut error_message)
} else {
None
};
Expand Down Expand Up @@ -108,8 +109,12 @@ impl RegisterDpnsNameScreen {
// Set the selected_qualified_identity to the found identity
self.selected_qualified_identity = Some(qi.clone());
// Update the selected wallet
self.selected_wallet =
get_selected_wallet(&qi.0, &self.app_context, &mut self.error_message);
self.selected_wallet = get_selected_wallet(
&qi.0,
Some(&self.app_context),
None,
&mut self.error_message,
);
} else {
// If not found, you might want to handle this case
// For now, we'll set selected_qualified_identity to None
Expand Down Expand Up @@ -179,7 +184,8 @@ impl RegisterDpnsNameScreen {
self.selected_qualified_identity = Some(qualified_identity.clone());
self.selected_wallet = get_selected_wallet(
&qualified_identity.0,
&self.app_context,
Some(&self.app_context),
None,
&mut self.error_message,
);
}
Expand Down Expand Up @@ -466,43 +472,3 @@ pub fn is_contested_name(name: &str) -> bool {
}
true
}

pub fn get_selected_wallet(
qualified_identity: &QualifiedIdentity,
app_context: &AppContext,
error_message: &mut Option<String>,
) -> Option<Arc<RwLock<Wallet>>> {
let dpns_contract = &app_context.dpns_contract;

let preorder_document_type = match dpns_contract.document_type_for_name("preorder") {
Ok(doc_type) => doc_type,
Err(e) => {
*error_message = Some(format!("DPNS preorder document type not found: {}", e));
return None;
}
};

let public_key = match qualified_identity.document_signing_key(&preorder_document_type) {
Some(key) => key,
None => {
*error_message = Some(
"Identity doesn't have an authentication key for signing document transitions"
.to_string(),
);
return None;
}
};

let key = (PrivateKeyTarget::PrivateKeyOnMainIdentity, public_key.id());

if let Some((_, PrivateKeyData::AtWalletDerivationPath(wallet_derivation_path))) =
qualified_identity.private_keys.private_keys.get(&key)
{
qualified_identity
.associated_wallets
.get(&wallet_derivation_path.wallet_seed_hash)
.cloned()
} else {
None
}
}
24 changes: 12 additions & 12 deletions src/ui/identities/transfers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ use std::sync::{Arc, RwLock};
use std::time::{SystemTime, UNIX_EPOCH};

use crate::ui::components::wallet_unlock::ScreenWithWalletUnlock;
use crate::ui::identities::register_dpns_name_screen::get_selected_wallet;

use super::get_selected_wallet;

pub enum TransferCreditsStatus {
NotStarted,
Expand All @@ -47,20 +48,19 @@ pub struct TransferScreen {
impl TransferScreen {
pub fn new(identity: QualifiedIdentity, app_context: &Arc<AppContext>) -> Self {
let max_amount = identity.identity.balance();
let selected_key = identity
.identity
.get_first_public_key_matching(
Purpose::TRANSFER,
SecurityLevel::full_range().into(),
KeyType::all_key_types().into(),
false,
)
.cloned();
let identity_clone = identity.identity.clone();
let selected_key = identity_clone.get_first_public_key_matching(
Purpose::TRANSFER,
SecurityLevel::full_range().into(),
KeyType::all_key_types().into(),
false,
);
let mut error_message = None;
let selected_wallet = get_selected_wallet(&identity, app_context, &mut error_message);
let selected_wallet =
get_selected_wallet(&identity, None, selected_key, &mut error_message);
Self {
identity,
selected_key,
selected_key: selected_key.cloned(),
receiver_identity_id: String::new(),
amount: String::new(),
transfer_credits_status: TransferCreditsStatus::NotStarted,
Expand Down
17 changes: 14 additions & 3 deletions src/ui/identities/withdraw_from_identity_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use std::str::FromStr;
use std::sync::{Arc, RwLock};
use std::time::{SystemTime, UNIX_EPOCH};

use super::get_selected_wallet;
use super::keys::key_info_screen::KeyInfoScreen;

pub enum WithdrawFromIdentityStatus {
Expand Down Expand Up @@ -48,19 +49,29 @@ pub struct WithdrawalScreen {
impl WithdrawalScreen {
pub fn new(identity: QualifiedIdentity, app_context: &Arc<AppContext>) -> Self {
let max_amount = identity.identity.balance();
let identity_clone = identity.identity.clone();
let selected_key = identity_clone.get_first_public_key_matching(
Purpose::TRANSFER,
SecurityLevel::full_range().into(),
KeyType::all_key_types().into(),
false,
);
let mut error_message = None;
let selected_wallet =
get_selected_wallet(&identity, None, selected_key, &mut error_message);
Self {
identity,
selected_key: None,
selected_key: selected_key.cloned(),
withdrawal_address: String::new(),
withdrawal_amount: String::new(),
max_amount,
app_context: app_context.clone(),
confirmation_popup: false,
withdraw_from_identity_status: WithdrawFromIdentityStatus::NotStarted,
selected_wallet: None,
selected_wallet,
wallet_password: String::new(),
show_password: false,
error_message: None,
error_message,
}
}

Expand Down