From 2f734a81269298595cea78fc660d4cb513354858 Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Wed, 8 Oct 2025 13:08:58 +0700 Subject: [PATCH 1/9] feat: derive keys from loaded wallets when loading identity --- src/backend_task/identity/load_identity.rs | 290 ++++++++++++++++-- src/backend_task/identity/mod.rs | 2 + .../add_existing_identity_screen.rs | 147 +++++++++ 3 files changed, 419 insertions(+), 20 deletions(-) diff --git a/src/backend_task/identity/load_identity.rs b/src/backend_task/identity/load_identity.rs index b82da7ad9..f6c6f0e6f 100644 --- a/src/backend_task/identity/load_identity.rs +++ b/src/backend_task/identity/load_identity.rs @@ -4,26 +4,35 @@ use crate::context::AppContext; use crate::model::qualified_identity::PrivateKeyTarget::{ self, PrivateKeyOnMainIdentity, PrivateKeyOnVoterIdentity, }; -use crate::model::qualified_identity::encrypted_key_storage::PrivateKeyData; +use crate::model::qualified_identity::encrypted_key_storage::{ + PrivateKeyData, WalletDerivationPath, +}; use crate::model::qualified_identity::qualified_identity_public_key::QualifiedIdentityPublicKey; use crate::model::qualified_identity::{ DPNSNameInfo, IdentityStatus, IdentityType, QualifiedIdentity, }; +use crate::model::wallet::{Wallet, WalletSeedHash}; use dash_sdk::Sdk; use dash_sdk::dashcore_rpc::dashcore::PrivateKey; use dash_sdk::dashcore_rpc::dashcore::key::Secp256k1; use dash_sdk::dpp::dashcore::hashes::Hash; use dash_sdk::dpp::document::DocumentV0Getters; use dash_sdk::dpp::identifier::MasternodeIdentifiers; +use dash_sdk::dpp::identity::KeyType; use dash_sdk::dpp::identity::SecurityLevel; use dash_sdk::dpp::identity::accessors::IdentityGettersV0; use dash_sdk::dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; +use dash_sdk::dpp::key_wallet::bip32::{DerivationPath, KeyDerivationType}; use dash_sdk::dpp::platform_value::Value; use dash_sdk::dpp::platform_value::string_encoding::Encoding; use dash_sdk::drive::query::{WhereClause, WhereOperator}; use dash_sdk::platform::{Document, DocumentQuery, Fetch, FetchMany, Identifier, Identity}; use egui::ahash::HashMap; use std::collections::BTreeMap; +use std::convert::TryInto; +use std::sync::{Arc, RwLock}; + +const MAX_IDENTITY_INDEX_SEARCH: u32 = 200; impl AppContext { pub(super) async fn load_identity( @@ -39,6 +48,8 @@ impl AppContext { owner_private_key_input, payout_address_private_key_input, keys_input, + derive_keys_from_wallets, + selected_wallet_seed_hash, } = input; // Verify the voting private key @@ -69,6 +80,16 @@ impl AppContext { let wallets = self.wallets.read().unwrap().clone(); + if identity_type == IdentityType::User && derive_keys_from_wallets { + if let Some((_, _, wallet_private_keys)) = self.match_user_identity_keys_with_wallet( + &identity, + &wallets, + selected_wallet_seed_hash, + )? { + encrypted_private_keys.extend(wallet_private_keys); + } + } + if identity_type != IdentityType::User && let Some(owner_private_key_bytes) = owner_private_key_bytes { @@ -198,44 +219,50 @@ impl AppContext { .unzip(); for (&key_id, public_key) in identity.public_keys().iter() { + let key_map_key = (PrivateKeyTarget::PrivateKeyOnMainIdentity, key_id); let qualified_key = QualifiedIdentityPublicKey::from_identity_public_key_with_wallets_check( public_key.clone(), self.network, &wallets.values().collect::>(), ); - - if let Some(wallet_derivation_path) = - qualified_key.in_wallet_at_derivation_path.clone() - { - encrypted_private_keys.insert( - (PrivateKeyTarget::PrivateKeyOnMainIdentity, key_id), - ( - qualified_key, - PrivateKeyData::AtWalletDerivationPath(wallet_derivation_path), - ), - ); - } else if let Some(private_key_bytes) = + if let Some(private_key_bytes) = public_key_lookup.get(public_key.data().0.as_slice()) { let private_data = match public_key.security_level() { SecurityLevel::MEDIUM => PrivateKeyData::AlwaysClear(*private_key_bytes), _ => PrivateKeyData::Clear(*private_key_bytes), }; - encrypted_private_keys.insert( - (PrivateKeyTarget::PrivateKeyOnMainIdentity, key_id), - (qualified_key, private_data), - ); - } else if let Some(private_key_bytes) = + encrypted_private_keys + .insert(key_map_key, (qualified_key.clone(), private_data)); + continue; + } + + if let Some(private_key_bytes) = public_key_hash_lookup.get(public_key.data().0.as_slice()) { let private_data = match public_key.security_level() { SecurityLevel::MEDIUM => PrivateKeyData::AlwaysClear(*private_key_bytes), _ => PrivateKeyData::Clear(*private_key_bytes), }; + encrypted_private_keys + .insert(key_map_key, (qualified_key.clone(), private_data)); + continue; + } + + if encrypted_private_keys.contains_key(&key_map_key) { + continue; + } + + if let Some(wallet_derivation_path) = + qualified_key.in_wallet_at_derivation_path.clone() + { encrypted_private_keys.insert( - (PrivateKeyTarget::PrivateKeyOnMainIdentity, key_id), - (qualified_key, private_data), + key_map_key, + ( + qualified_key, + PrivateKeyData::AtWalletDerivationPath(wallet_derivation_path), + ), ); } } @@ -317,8 +344,231 @@ impl AppContext { self.insert_local_qualified_identity(&qualified_identity, &wallet_info) .map_err(|e| format!("Database error: {}", e))?; + if let Some((wallet_seed_hash, identity_index)) = wallet_info { + if let Some(wallet_arc) = wallets.get(&wallet_seed_hash) { + let mut wallet = wallet_arc.write().unwrap(); + wallet + .identities + .insert(identity_index, qualified_identity.identity.clone()); + } + } + Ok(BackendTaskSuccessResult::Message( "Successfully loaded identity".to_string(), )) } + + fn match_user_identity_keys_with_wallet( + &self, + identity: &Identity, + wallets: &BTreeMap>>, + wallet_filter: Option, + ) -> Result< + Option<( + WalletSeedHash, + u32, + BTreeMap<(PrivateKeyTarget, u32), (QualifiedIdentityPublicKey, PrivateKeyData)>, + )>, + String, + > { + let highest_identity_key_id = identity.public_keys().keys().copied().max().unwrap_or(0); + let top_bound = highest_identity_key_id.saturating_add(6).max(1); + + for (&wallet_seed_hash, wallet_arc) in wallets.iter() { + if wallet_filter.is_some_and(|filter| filter != wallet_seed_hash) { + continue; + } + let mut wallet = wallet_arc.write().unwrap(); + if !wallet.is_open() { + continue; + } + + if let Some((identity_index, wallet_private_keys)) = self + .attempt_match_identity_with_wallet( + identity, + &mut wallet, + wallet_seed_hash, + top_bound, + )? + { + drop(wallet); + return Ok(Some(( + wallet_seed_hash, + identity_index, + wallet_private_keys, + ))); + } + } + + Ok(None) + } + + fn attempt_match_identity_with_wallet( + &self, + identity: &Identity, + wallet: &mut Wallet, + wallet_seed_hash: WalletSeedHash, + top_bound: u32, + ) -> Result< + Option<( + u32, + BTreeMap<(PrivateKeyTarget, u32), (QualifiedIdentityPublicKey, PrivateKeyData)>, + )>, + String, + > { + let identity_id = identity.id(); + + if let Some((&identity_index, _)) = wallet + .identities + .iter() + .find(|(_, existing)| existing.id() == identity_id) + { + let (public_key_map, public_key_hash_map) = wallet + .identity_authentication_ecdsa_public_keys_data_map( + self.network, + identity_index, + 0..top_bound, + Some(self), + )?; + let wallet_private_keys = self.build_wallet_private_key_map( + identity, + wallet_seed_hash, + identity_index, + &public_key_map, + &public_key_hash_map, + ); + + if !wallet_private_keys.is_empty() { + return Ok(Some((identity_index, wallet_private_keys))); + } + } + + for candidate_index in 0..=MAX_IDENTITY_INDEX_SEARCH { + let (public_key_map, public_key_hash_map) = wallet + .identity_authentication_ecdsa_public_keys_data_map( + self.network, + candidate_index, + 0..top_bound, + None, + )?; + + if !Self::identity_matches_wallet_key_material( + identity, + &public_key_map, + &public_key_hash_map, + ) { + continue; + } + + let (public_key_map, public_key_hash_map) = wallet + .identity_authentication_ecdsa_public_keys_data_map( + self.network, + candidate_index, + 0..top_bound, + Some(self), + )?; + + let wallet_private_keys = self.build_wallet_private_key_map( + identity, + wallet_seed_hash, + candidate_index, + &public_key_map, + &public_key_hash_map, + ); + + if wallet_private_keys.is_empty() { + continue; + } + + return Ok(Some((candidate_index, wallet_private_keys))); + } + + Ok(None) + } + + fn identity_matches_wallet_key_material( + identity: &Identity, + public_key_map: &BTreeMap, u32>, + public_key_hash_map: &BTreeMap<[u8; 20], u32>, + ) -> bool { + identity + .public_keys() + .values() + .any(|public_key| match public_key.key_type() { + KeyType::ECDSA_SECP256K1 => { + if public_key_map.contains_key(public_key.data().as_slice()) { + true + } else if let Ok(hash) = <[u8; 20]>::try_from(public_key.data().as_slice()) { + public_key_hash_map.contains_key(&hash) + } else { + false + } + } + KeyType::ECDSA_HASH160 => { + if let Ok(hash) = <[u8; 20]>::try_from(public_key.data().as_slice()) { + public_key_hash_map.contains_key(&hash) + } else { + false + } + } + _ => false, + }) + } + + fn build_wallet_private_key_map( + &self, + identity: &Identity, + wallet_seed_hash: WalletSeedHash, + identity_index: u32, + public_key_map: &BTreeMap, u32>, + public_key_hash_map: &BTreeMap<[u8; 20], u32>, + ) -> BTreeMap<(PrivateKeyTarget, u32), (QualifiedIdentityPublicKey, PrivateKeyData)> { + identity + .public_keys() + .values() + .filter_map(|public_key| { + let index = + match public_key.key_type() { + KeyType::ECDSA_SECP256K1 => public_key_map + .get(public_key.data().as_slice()) + .copied() + .or_else(|| { + public_key.data().as_slice().try_into().ok().and_then( + |hash: [u8; 20]| public_key_hash_map.get(&hash).copied(), + ) + }), + KeyType::ECDSA_HASH160 => public_key + .data() + .as_slice() + .try_into() + .ok() + .and_then(|hash: [u8; 20]| public_key_hash_map.get(&hash).copied()), + _ => None, + }?; + + let derivation_path = DerivationPath::identity_authentication_path( + self.network, + KeyDerivationType::ECDSA, + identity_index, + index, + ); + + let wallet_derivation_path = WalletDerivationPath { + wallet_seed_hash, + derivation_path, + }; + + Some(( + (PrivateKeyTarget::PrivateKeyOnMainIdentity, public_key.id()), + ( + QualifiedIdentityPublicKey::from_identity_public_key_in_wallet( + public_key.clone(), + Some(wallet_derivation_path.clone()), + ), + PrivateKeyData::AtWalletDerivationPath(wallet_derivation_path), + ), + )) + }) + .collect() + } } diff --git a/src/backend_task/identity/mod.rs b/src/backend_task/identity/mod.rs index e1b50b5d2..c31664fb9 100644 --- a/src/backend_task/identity/mod.rs +++ b/src/backend_task/identity/mod.rs @@ -43,6 +43,8 @@ pub struct IdentityInputToLoad { pub owner_private_key_input: String, pub payout_address_private_key_input: String, pub keys_input: Vec, + pub derive_keys_from_wallets: bool, + pub selected_wallet_seed_hash: Option, } #[derive(Debug, Clone, PartialEq)] diff --git a/src/ui/identities/add_existing_identity_screen.rs b/src/ui/identities/add_existing_identity_screen.rs index 4b34b8947..83dcea0de 100644 --- a/src/ui/identities/add_existing_identity_screen.rs +++ b/src/ui/identities/add_existing_identity_screen.rs @@ -73,6 +73,7 @@ pub struct AddExistingIdentityScreen { add_identity_status: AddIdentityStatus, testnet_loaded_nodes: Option, selected_wallet: Option>>, + identity_associated_with_wallet: bool, show_password: bool, wallet_password: String, error_message: Option, @@ -100,6 +101,7 @@ impl AddExistingIdentityScreen { add_identity_status: AddIdentityStatus::NotStarted, testnet_loaded_nodes, selected_wallet, + identity_associated_with_wallet: false, show_password: false, wallet_password: "".to_string(), error_message: None, @@ -122,6 +124,23 @@ impl AddExistingIdentityScreen { ui.add_space(10.0); } + let wallets_snapshot: Vec<(String, Arc>)> = { + let wallets_guard = self.app_context.wallets.read().unwrap(); + wallets_guard + .values() + .map(|wallet| { + let alias = wallet + .read() + .unwrap() + .alias + .clone() + .unwrap_or_else(|| "Unnamed Wallet".to_string()); + (alias, wallet.clone()) + }) + .collect() + }; + let has_wallets = !wallets_snapshot.is_empty(); + egui::Grid::new("add_existing_identity_grid") .num_columns(2) .spacing([10.0, 10.0]) @@ -228,6 +247,124 @@ impl AddExistingIdentityScreen { }); ui.add_space(10.0); + ui.vertical(|ui| { + ui.horizontal(|ui| { + let checkbox_response = ui.checkbox( + &mut self.identity_associated_with_wallet, + "Identity may be associated with a loaded wallet", + ); + let response = crate::ui::helpers::info_icon_button( + ui, + "When enabled, Dash Evo Tool scans the selected unlocked wallet (or all unlocked wallets) right now to find matching keys.", + ); + if response.clicked() { + self.show_pop_up_info = Some( + "When enabled, Dash Evo Tool scans the selected unlocked wallet (or all unlocked wallets) right now to find matching keys." + .to_string(), + ); + } + + if checkbox_response.changed() && !self.identity_associated_with_wallet { + self.selected_wallet = None; + } + }); + + if self.identity_associated_with_wallet { + if has_wallets { + let selected_label = self + .selected_wallet + .as_ref() + .and_then(|selected| { + wallets_snapshot.iter().find_map(|(alias, wallet)| { + if Arc::ptr_eq(selected, wallet) { + Some(alias.clone()) + } else { + None + } + }) + }) + .unwrap_or_else(|| "All unlocked wallets".to_string()); + + ComboBox::from_id_salt("identity_wallet_selector") + .selected_text(selected_label) + .show_ui(ui, |ui| { + if ui + .selectable_label( + self.selected_wallet.is_none(), + "All unlocked wallets", + ) + .clicked() + { + self.selected_wallet = None; + } + + for (alias, wallet) in &wallets_snapshot { + let is_selected = self + .selected_wallet + .as_ref() + .is_some_and(|selected| Arc::ptr_eq(selected, wallet)); + + if ui.selectable_label(is_selected, alias).clicked() { + self.selected_wallet = Some(wallet.clone()); + } + } + }); + + if let Some(selected_wallet) = &self.selected_wallet { + let wallet_still_loaded = wallets_snapshot + .iter() + .any(|(_, wallet)| Arc::ptr_eq(wallet, selected_wallet)); + + if wallet_still_loaded { + let (needed_unlock, just_unlocked) = + self.render_wallet_unlock_if_needed(ui); + if needed_unlock && !just_unlocked { + ui.colored_label( + Color32::YELLOW, + "Unlock this wallet to allow Dash Evo Tool to derive the keys.", + ); + } else if just_unlocked { + ui.colored_label( + Color32::GREEN, + "Wallet unlocked. We'll pull any matching keys automatically.", + ); + } + } else { + self.selected_wallet = None; + ui.colored_label( + Color32::RED, + "Selected wallet is no longer loaded. We'll search unlocked wallets instead.", + ); + } + } else { + ui.colored_label( + Color32::GRAY, + "We'll scan every unlocked wallet for matching keys during this load.", + ); + } + } else { + ui.colored_label( + Color32::GRAY, + "No wallets are currently loaded. Unlock or import one to scan for keys.", + ); + } + } + + if self.identity_associated_with_wallet { + ui.colored_label( + Color32::GRAY, + "Only unlocked wallets are scanned. Leave the key fields empty unless you need to add a manual key.", + ); + } else { + ui.colored_label( + Color32::GRAY, + "Wallet scanning is skipped for this load. Add keys manually or enable the option above to search wallets.", + ); + } + }); + + ui.add_space(10.0); + // Add button to add more keys if ui.button("+ Add Key").clicked() { self.keys_input.push(String::new()); @@ -354,6 +491,14 @@ impl AddExistingIdentityScreen { } fn load_identity_clicked(&mut self) -> AppAction { + let selected_wallet_seed_hash = if self.identity_associated_with_wallet { + self.selected_wallet + .as_ref() + .map(|wallet| wallet.read().unwrap().seed_hash()) + } else { + None + }; + let identity_input = IdentityInputToLoad { identity_id_input: self.identity_id_input.trim().to_string(), identity_type: self.identity_type, @@ -362,6 +507,8 @@ impl AddExistingIdentityScreen { owner_private_key_input: self.owner_private_key_input.clone(), payout_address_private_key_input: self.payout_address_private_key_input.clone(), keys_input: self.keys_input.clone(), + derive_keys_from_wallets: self.identity_associated_with_wallet, + selected_wallet_seed_hash, }; AppAction::BackendTask(BackendTask::IdentityTask(IdentityTask::LoadIdentity( From 549db993e33a074deda3987bf016987877131b95 Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Wed, 8 Oct 2025 13:13:14 +0700 Subject: [PATCH 2/9] clippy --- src/backend_task/identity/load_identity.rs | 47 +++++++++------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/src/backend_task/identity/load_identity.rs b/src/backend_task/identity/load_identity.rs index f6c6f0e6f..5bafc1b72 100644 --- a/src/backend_task/identity/load_identity.rs +++ b/src/backend_task/identity/load_identity.rs @@ -34,6 +34,9 @@ use std::sync::{Arc, RwLock}; const MAX_IDENTITY_INDEX_SEARCH: u32 = 200; +type WalletKeyMap = BTreeMap<(PrivateKeyTarget, u32), (QualifiedIdentityPublicKey, PrivateKeyData)>; +type WalletMatchResult = Option<(WalletSeedHash, u32, WalletKeyMap)>; + impl AppContext { pub(super) async fn load_identity( &self, @@ -80,14 +83,15 @@ impl AppContext { let wallets = self.wallets.read().unwrap().clone(); - if identity_type == IdentityType::User && derive_keys_from_wallets { - if let Some((_, _, wallet_private_keys)) = self.match_user_identity_keys_with_wallet( + if identity_type == IdentityType::User + && derive_keys_from_wallets + && let Some((_, _, wallet_private_keys)) = self.match_user_identity_keys_with_wallet( &identity, &wallets, selected_wallet_seed_hash, - )? { - encrypted_private_keys.extend(wallet_private_keys); - } + )? + { + encrypted_private_keys.extend(wallet_private_keys); } if identity_type != IdentityType::User @@ -344,13 +348,13 @@ impl AppContext { self.insert_local_qualified_identity(&qualified_identity, &wallet_info) .map_err(|e| format!("Database error: {}", e))?; - if let Some((wallet_seed_hash, identity_index)) = wallet_info { - if let Some(wallet_arc) = wallets.get(&wallet_seed_hash) { - let mut wallet = wallet_arc.write().unwrap(); - wallet - .identities - .insert(identity_index, qualified_identity.identity.clone()); - } + if let Some((wallet_seed_hash, identity_index)) = wallet_info + && let Some(wallet_arc) = wallets.get(&wallet_seed_hash) + { + let mut wallet = wallet_arc.write().unwrap(); + wallet + .identities + .insert(identity_index, qualified_identity.identity.clone()); } Ok(BackendTaskSuccessResult::Message( @@ -363,14 +367,7 @@ impl AppContext { identity: &Identity, wallets: &BTreeMap>>, wallet_filter: Option, - ) -> Result< - Option<( - WalletSeedHash, - u32, - BTreeMap<(PrivateKeyTarget, u32), (QualifiedIdentityPublicKey, PrivateKeyData)>, - )>, - String, - > { + ) -> Result { let highest_identity_key_id = identity.public_keys().keys().copied().max().unwrap_or(0); let top_bound = highest_identity_key_id.saturating_add(6).max(1); @@ -409,13 +406,7 @@ impl AppContext { wallet: &mut Wallet, wallet_seed_hash: WalletSeedHash, top_bound: u32, - ) -> Result< - Option<( - u32, - BTreeMap<(PrivateKeyTarget, u32), (QualifiedIdentityPublicKey, PrivateKeyData)>, - )>, - String, - > { + ) -> Result, String> { let identity_id = identity.id(); if let Some((&identity_index, _)) = wallet @@ -522,7 +513,7 @@ impl AppContext { identity_index: u32, public_key_map: &BTreeMap, u32>, public_key_hash_map: &BTreeMap<[u8; 20], u32>, - ) -> BTreeMap<(PrivateKeyTarget, u32), (QualifiedIdentityPublicKey, PrivateKeyData)> { + ) -> WalletKeyMap { identity .public_keys() .values() From a63814eb25c1b704a8890e9cc9a549656eac7e4c Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Tue, 28 Oct 2025 13:52:50 +0700 Subject: [PATCH 3/9] chore: update platform version --- Cargo.lock | 198 ++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 100 insertions(+), 100 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0292c5cf5..71a7c7e63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1645,13 +1645,13 @@ dependencies = [ [[package]] name = "dapi-grpc" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ - "dapi-grpc-macros 2.1.0-rc.1", + "dapi-grpc-macros 2.1.2", "futures-core", "getrandom 0.2.16", - "platform-version 2.1.0-rc.1", + "platform-version 2.1.2", "prost 0.14.1", "serde", "serde_bytes", @@ -1674,8 +1674,8 @@ dependencies = [ [[package]] name = "dapi-grpc-macros" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "heck", "quote", @@ -1733,11 +1733,11 @@ dependencies = [ [[package]] name = "dash-context-provider" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ - "dpp 2.1.0-rc.1", - "drive 2.1.0-rc.1", + "dpp 2.1.2", + "drive 2.1.2", "hex", "serde", "serde_json", @@ -1759,7 +1759,7 @@ dependencies = [ "chrono-humanize", "crossbeam-channel", "dark-light", - "dash-sdk 2.1.0-rc.1", + "dash-sdk 2.1.2", "derive_more 2.0.1", "directories", "dotenvy", @@ -1854,8 +1854,8 @@ dependencies = [ [[package]] name = "dash-sdk" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "arc-swap", "async-trait", @@ -1863,21 +1863,21 @@ dependencies = [ "bip37-bloom-filter", "chrono", "ciborium", - "dapi-grpc 2.1.0-rc.1", - "dapi-grpc-macros 2.1.0-rc.1", + "dapi-grpc 2.1.2", + "dapi-grpc-macros 2.1.2", "dash-context-provider", "derive_more 1.0.0", "dotenvy", - "dpp 2.1.0-rc.1", - "drive 2.1.0-rc.1", - "drive-proof-verifier 2.1.0-rc.1", + "dpp 2.1.2", + "drive 2.1.2", + "drive-proof-verifier 2.1.2", "envy", "futures", "hex", "http", "js-sys", "lru", - "rs-dapi-client 2.1.0-rc.1", + "rs-dapi-client 2.1.2", "rustls-pemfile", "serde", "serde_json", @@ -2048,11 +2048,11 @@ dependencies = [ [[package]] name = "dashpay-contract" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ - "platform-value 2.1.0-rc.1", - "platform-version 2.1.0-rc.1", + "platform-value 2.1.2", + "platform-version 2.1.2", "serde_json", "thiserror 2.0.17", ] @@ -2078,21 +2078,21 @@ dependencies = [ [[package]] name = "data-contracts" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" -dependencies = [ - "dashpay-contract 2.1.0-rc.1", - "dpns-contract 2.1.0-rc.1", - "feature-flags-contract 2.1.0-rc.1", - "keyword-search-contract 2.1.0-rc.1", - "masternode-reward-shares-contract 2.1.0-rc.1", - "platform-value 2.1.0-rc.1", - "platform-version 2.1.0-rc.1", +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" +dependencies = [ + "dashpay-contract 2.1.2", + "dpns-contract 2.1.2", + "feature-flags-contract 2.1.2", + "keyword-search-contract 2.1.2", + "masternode-reward-shares-contract 2.1.2", + "platform-value 2.1.2", + "platform-version 2.1.2", "serde_json", "thiserror 2.0.17", - "token-history-contract 2.1.0-rc.1", - "wallet-utils-contract 2.1.0-rc.1", - "withdrawals-contract 2.1.0-rc.1", + "token-history-contract 2.1.2", + "wallet-utils-contract 2.1.2", + "withdrawals-contract 2.1.2", ] [[package]] @@ -2339,11 +2339,11 @@ dependencies = [ [[package]] name = "dpns-contract" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ - "platform-value 2.1.0-rc.1", - "platform-version 2.1.0-rc.1", + "platform-value 2.1.2", + "platform-version 2.1.2", "serde_json", "thiserror 2.0.17", ] @@ -2393,8 +2393,8 @@ dependencies = [ [[package]] name = "dpp" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "anyhow", "async-trait", @@ -2408,7 +2408,7 @@ dependencies = [ "ciborium", "dashcore 0.40.0", "dashcore-rpc 0.40.0", - "data-contracts 2.1.0-rc.1", + "data-contracts 2.1.2", "derive_more 1.0.0", "env_logger", "getrandom 0.2.16", @@ -2421,11 +2421,11 @@ dependencies = [ "nohash-hasher", "num_enum 0.7.5", "once_cell", - "platform-serialization 2.1.0-rc.1", - "platform-serialization-derive 2.1.0-rc.1", - "platform-value 2.1.0-rc.1", - "platform-version 2.1.0-rc.1", - "platform-versioning 2.1.0-rc.1", + "platform-serialization 2.1.2", + "platform-serialization-derive 2.1.2", + "platform-value 2.1.2", + "platform-version 2.1.2", + "platform-versioning 2.1.2", "rand 0.8.5", "regex", "serde", @@ -2464,13 +2464,13 @@ dependencies = [ [[package]] name = "drive" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "bincode 2.0.0-rc.3", "byteorder", "derive_more 1.0.0", - "dpp 2.1.0-rc.1", + "dpp 2.1.2", "grovedb", "grovedb-costs", "grovedb-epoch-based-storage-flags", @@ -2480,7 +2480,7 @@ dependencies = [ "indexmap 2.12.0", "integer-encoding", "nohash-hasher", - "platform-version 2.1.0-rc.1", + "platform-version 2.1.2", "serde", "sqlparser", "thiserror 2.0.17", @@ -2510,19 +2510,19 @@ dependencies = [ [[package]] name = "drive-proof-verifier" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "bincode 2.0.0-rc.3", - "dapi-grpc 2.1.0-rc.1", + "dapi-grpc 2.1.2", "dash-context-provider", "derive_more 1.0.0", - "dpp 2.1.0-rc.1", - "drive 2.1.0-rc.1", + "dpp 2.1.2", + "drive 2.1.2", "hex", "indexmap 2.12.0", - "platform-serialization 2.1.0-rc.1", - "platform-serialization-derive 2.1.0-rc.1", + "platform-serialization 2.1.2", + "platform-serialization-derive 2.1.2", "serde", "serde_json", "tenderdash-abci 1.5.0-dev.2", @@ -3090,11 +3090,11 @@ dependencies = [ [[package]] name = "feature-flags-contract" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ - "platform-value 2.1.0-rc.1", - "platform-version 2.1.0-rc.1", + "platform-value 2.1.2", + "platform-version 2.1.2", "serde_json", "thiserror 2.0.17", ] @@ -4452,11 +4452,11 @@ dependencies = [ [[package]] name = "keyword-search-contract" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ - "platform-value 2.1.0-rc.1", - "platform-version 2.1.0-rc.1", + "platform-value 2.1.2", + "platform-version 2.1.2", "serde_json", "thiserror 2.0.17", ] @@ -4672,11 +4672,11 @@ dependencies = [ [[package]] name = "masternode-reward-shares-contract" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ - "platform-value 2.1.0-rc.1", - "platform-version 2.1.0-rc.1", + "platform-value 2.1.2", + "platform-version 2.1.2", "serde_json", "thiserror 2.0.17", ] @@ -5713,11 +5713,11 @@ dependencies = [ [[package]] name = "platform-serialization" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "bincode 2.0.0-rc.3", - "platform-version 2.1.0-rc.1", + "platform-version 2.1.2", ] [[package]] @@ -5733,8 +5733,8 @@ dependencies = [ [[package]] name = "platform-serialization-derive" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "proc-macro2", "quote", @@ -5764,8 +5764,8 @@ dependencies = [ [[package]] name = "platform-value" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "base64 0.22.1", "bincode 2.0.0-rc.3", @@ -5773,8 +5773,8 @@ dependencies = [ "ciborium", "hex", "indexmap 2.12.0", - "platform-serialization 2.1.0-rc.1", - "platform-version 2.1.0-rc.1", + "platform-serialization 2.1.2", + "platform-version 2.1.2", "rand 0.8.5", "serde", "serde_json", @@ -5796,8 +5796,8 @@ dependencies = [ [[package]] name = "platform-version" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "bincode 2.0.0-rc.3", "grovedb-version", @@ -5818,8 +5818,8 @@ dependencies = [ [[package]] name = "platform-versioning" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "proc-macro2", "quote", @@ -6454,12 +6454,12 @@ dependencies = [ [[package]] name = "rs-dapi-client" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "backon", "chrono", - "dapi-grpc 2.1.0-rc.1", + "dapi-grpc 2.1.2", "futures", "getrandom 0.2.16", "gloo-timers", @@ -7558,11 +7558,11 @@ dependencies = [ [[package]] name = "token-history-contract" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ - "platform-value 2.1.0-rc.1", - "platform-version 2.1.0-rc.1", + "platform-value 2.1.2", + "platform-version 2.1.2", "serde_json", "thiserror 2.0.17", ] @@ -8311,11 +8311,11 @@ dependencies = [ [[package]] name = "wallet-utils-contract" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ - "platform-value 2.1.0-rc.1", - "platform-version 2.1.0-rc.1", + "platform-value 2.1.2", + "platform-version 2.1.2", "serde_json", "thiserror 2.0.17", ] @@ -9540,12 +9540,12 @@ dependencies = [ [[package]] name = "withdrawals-contract" -version = "2.1.0-rc.1" -source = "git+https://www.github.com/dashpay/platform?rev=68f77c085643edb9a46afd4ff51c9d20b4ce2912#68f77c085643edb9a46afd4ff51c9d20b4ce2912" +version = "2.1.2" +source = "git+https://www.github.com/dashpay/platform?tag=v2.1.2#f49390fb4e44d2591066debde3e26d452f9d1ea2" dependencies = [ "num_enum 0.5.11", - "platform-value 2.1.0-rc.1", - "platform-version 2.1.0-rc.1", + "platform-value 2.1.2", + "platform-version 2.1.2", "serde", "serde_json", "serde_repr", diff --git a/Cargo.toml b/Cargo.toml index c97db8f9c..d54a8ed1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ qrcode = "0.14.1" nix = { version = "0.30.1", features = ["signal"] } eframe = { version = "0.32.0", features = ["persistence"] } base64 = "0.22.1" -dash-sdk = { git = "https://www.github.com/dashpay/platform", rev = "68f77c085643edb9a46afd4ff51c9d20b4ce2912", features = ["core_key_wallet", "core_bincode", "core_quorum-validation", "core_verification", "core_rpc_client"] } +dash-sdk = { git = "https://www.github.com/dashpay/platform", tag = "v2.1.2", features = ["core_key_wallet", "core_bincode", "core_quorum-validation", "core_verification", "core_rpc_client"] } grovestark = { git = "https://www.github.com/pauldelucia/grovestark", rev = "5313ba9df590f114e11934e281f1e8c8bc462794" } rayon = "1.8" thiserror = "2.0.12" From 9830efbf1117f0c35a9e4fb61ce32b5bc1d7f28c Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Tue, 28 Oct 2025 14:30:57 +0700 Subject: [PATCH 4/9] ok --- src/ui/identities/add_existing_identity_screen.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui/identities/add_existing_identity_screen.rs b/src/ui/identities/add_existing_identity_screen.rs index da6490edc..44aaed6f3 100644 --- a/src/ui/identities/add_existing_identity_screen.rs +++ b/src/ui/identities/add_existing_identity_screen.rs @@ -271,7 +271,7 @@ impl AddExistingIdentityScreen { ui.horizontal(|ui| { let checkbox_response = ui.checkbox( &mut self.identity_associated_with_wallet, - "Identity may be associated with a loaded wallet", + "Try to automatically derive keys from unlocked loaded wallets", ); let response = crate::ui::helpers::info_icon_button( ui, @@ -340,8 +340,8 @@ impl AddExistingIdentityScreen { self.render_wallet_unlock_if_needed(ui); if needed_unlock && !just_unlocked { ui.colored_label( - Color32::YELLOW, - "Unlock this wallet to allow Dash Evo Tool to derive the keys.", + Color32::DARK_RED, + "Press return/enter after typing the password.", ); } else if just_unlocked { ui.colored_label( From a65b600e2ddb32462f0ac27ac734a15d8b859ada Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Tue, 28 Oct 2025 16:26:46 +0700 Subject: [PATCH 5/9] cleanup ui --- .../add_existing_identity_screen.rs | 245 +++++++++--------- 1 file changed, 120 insertions(+), 125 deletions(-) diff --git a/src/ui/identities/add_existing_identity_screen.rs b/src/ui/identities/add_existing_identity_screen.rs index 44aaed6f3..7639f7c8e 100644 --- a/src/ui/identities/add_existing_identity_screen.rs +++ b/src/ui/identities/add_existing_identity_screen.rs @@ -113,11 +113,11 @@ impl AddExistingIdentityScreen { voting_private_key_input: String::new(), owner_private_key_input: String::new(), payout_address_private_key_input: String::new(), - keys_input: vec![String::new(), String::new(), String::new()], + keys_input: vec![], add_identity_status: AddIdentityStatus::NotStarted, testnet_loaded_nodes, selected_wallet, - identity_associated_with_wallet: false, + identity_associated_with_wallet: true, show_password: false, wallet_password: "".to_string(), error_message: None, @@ -160,118 +160,15 @@ impl AddExistingIdentityScreen { .collect() }; let has_wallets = !wallets_snapshot.is_empty(); + let mut should_return_early = false; - egui::Grid::new("add_existing_identity_grid") - .num_columns(2) - .spacing([10.0, 10.0]) - .striped(false) - .show(ui, |ui| { - ui.label("Identity ID / ProTxHash (Hex or Base58):"); - ui.text_edit_singleline(&mut self.identity_id_input); - ui.label(""); - ui.end_row(); - - ui.label("Identity Type:"); - egui::ComboBox::from_id_salt("identity_type_selector") - .selected_text(format!("{:?}", self.identity_type)) - // .width(350.0) // This sets the entire row's width - .show_ui(ui, |ui| { - ui.selectable_value(&mut self.identity_type, IdentityType::User, "User"); - ui.selectable_value( - &mut self.identity_type, - IdentityType::Masternode, - "Masternode", - ); - ui.selectable_value( - &mut self.identity_type, - IdentityType::Evonode, - "Evonode", - ); - }); - ui.label(""); - ui.end_row(); - - // Input for Alias - ui.horizontal(|ui| { - ui.label("Alias (optional):"); - let response = crate::ui::helpers::info_icon_button(ui, "Alias is optional. It is only used to help identify the identity in Dash Evo Tool. It isn't saved to Dash Platform."); - if response.clicked() { - self.show_pop_up_info = Some("Alias is optional. It is only used to help identify the identity in Dash Evo Tool. It isn't saved to Dash Platform.".to_string()); - } - }); - ui.text_edit_singleline(&mut self.alias_input); - ui.label(""); - ui.end_row(); - - // Render the keys input based on identity type - match self.identity_type { - IdentityType::Masternode | IdentityType::Evonode => { - // Store the voting and owner private key references before borrowing `self` mutably - let voting_private_key_input = &mut self.voting_private_key_input; - let owner_private_key_input = &mut self.owner_private_key_input; - let payout_address_private_key_input = - &mut self.payout_address_private_key_input; - - ui.label("Voting Private Key:"); - ui.text_edit_singleline(voting_private_key_input); - ui.end_row(); - - ui.label("Owner Private Key:"); - ui.text_edit_singleline(owner_private_key_input); - ui.end_row(); - - ui.label("Payout Address Private Key:"); - ui.text_edit_singleline(payout_address_private_key_input); - ui.end_row(); - } - IdentityType::User => { - // A temporary vector to store indices of keys to be removed - let mut keys_to_remove = vec![]; - - for (i, key) in self.keys_input.iter_mut().enumerate() { - // First column: the label & info icon, combined horizontally - ui.horizontal(|ui| { - ui.label(format!("Private Key {} (Hex or WIF):", i + 1)); - - let response = crate::ui::helpers::info_icon_button(ui, "You don't need to add all or even any private keys here. \ - Private keys can be added later. However, without private keys, \ - you won't be able to sign any transactions."); - - if response.clicked() { - self.show_pop_up_info = Some( - "You don't need to add all or even any private keys here. \ - Private keys can be added later. However, without private keys, \ - you won't be able to sign any transactions." - .to_string(), - ); - } - }); - - // Second column: the text field - ui.text_edit_singleline(key); - - // Third column: the remove button - if ui.button("-").clicked() { - keys_to_remove.push(i); - } - - ui.end_row(); - } - - // Remove the keys after the loop to avoid borrowing conflicts - for i in keys_to_remove.iter().rev() { - self.keys_input.remove(*i); - } - } - } - }); ui.add_space(10.0); ui.vertical(|ui| { ui.horizontal(|ui| { let checkbox_response = ui.checkbox( &mut self.identity_associated_with_wallet, - "Try to automatically derive keys from unlocked loaded wallets", + "Try to automatically derive private keys from loaded wallet", ); let response = crate::ui::helpers::info_icon_button( ui, @@ -339,6 +236,7 @@ impl AddExistingIdentityScreen { let (needed_unlock, just_unlocked) = self.render_wallet_unlock_if_needed(ui); if needed_unlock && !just_unlocked { + should_return_early = true; ui.colored_label( Color32::DARK_RED, "Press return/enter after typing the password.", @@ -356,11 +254,6 @@ impl AddExistingIdentityScreen { "Selected wallet is no longer loaded. We'll search unlocked wallets instead.", ); } - } else { - ui.colored_label( - Color32::GRAY, - "We'll scan every unlocked wallet for matching keys during this load.", - ); } } else { ui.colored_label( @@ -369,24 +262,126 @@ impl AddExistingIdentityScreen { ); } } - - if self.identity_associated_with_wallet { - ui.colored_label( - Color32::GRAY, - "Only unlocked wallets are scanned. Leave the key fields empty unless you need to add a manual key.", - ); - } else { - ui.colored_label( - Color32::GRAY, - "Wallet scanning is skipped for this load. Add keys manually or enable the option above to search wallets.", - ); - } }); + if should_return_early { + return action; + } + + ui.add_space(10.0); + + egui::Grid::new("add_existing_identity_grid") + .num_columns(2) + .spacing([10.0, 10.0]) + .striped(false) + .show(ui, |ui| { + ui.label("Identity ID / ProTxHash (Hex or Base58):"); + ui.text_edit_singleline(&mut self.identity_id_input); + ui.label(""); + ui.end_row(); + + ui.label("Identity Type:"); + + ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| { + egui::ComboBox::from_id_salt("identity_type_selector") + .selected_text(format!("{:?}", self.identity_type)) + // .width(350.0) // This sets the entire row's width + .show_ui(ui, |ui| { + ui.selectable_value(&mut self.identity_type, IdentityType::User, "User"); + ui.selectable_value( + &mut self.identity_type, + IdentityType::Masternode, + "Masternode", + ); + ui.selectable_value( + &mut self.identity_type, + IdentityType::Evonode, + "Evonode", + ); + }); + }); + ui.label(""); + ui.end_row(); + + // Input for Alias + ui.horizontal(|ui| { + ui.label("Alias (optional):"); + let response = crate::ui::helpers::info_icon_button(ui, "Alias is optional. It is only used to help identify the identity in Dash Evo Tool. It isn't saved to Dash Platform."); + if response.clicked() { + self.show_pop_up_info = Some("Alias is optional. It is only used to help identify the identity in Dash Evo Tool. It isn't saved to Dash Platform.".to_string()); + } + }); + ui.text_edit_singleline(&mut self.alias_input); + ui.label(""); + ui.end_row(); + + // Render the keys input based on identity type + match self.identity_type { + IdentityType::Masternode | IdentityType::Evonode => { + // Store the voting and owner private key references before borrowing `self` mutably + let voting_private_key_input = &mut self.voting_private_key_input; + let owner_private_key_input = &mut self.owner_private_key_input; + let payout_address_private_key_input = + &mut self.payout_address_private_key_input; + + ui.label("Voting Private Key:"); + ui.text_edit_singleline(voting_private_key_input); + ui.end_row(); + + ui.label("Owner Private Key:"); + ui.text_edit_singleline(owner_private_key_input); + ui.end_row(); + + ui.label("Payout Address Private Key:"); + ui.text_edit_singleline(payout_address_private_key_input); + ui.end_row(); + } + IdentityType::User => { + // A temporary vector to store indices of keys to be removed + let mut keys_to_remove = vec![]; + + for (i, key) in self.keys_input.iter_mut().enumerate() { + // First column: the label & info icon, combined horizontally + ui.horizontal(|ui| { + ui.label(format!("Private Key {} (Hex or WIF):", i + 1)); + + let response = crate::ui::helpers::info_icon_button(ui, "You don't need to add all or even any private keys here. \ + Private keys can be added later. However, without private keys, \ + you won't be able to sign any transactions."); + + if response.clicked() { + self.show_pop_up_info = Some( + "You don't need to add all or even any private keys here. \ + Private keys can be added later. However, without private keys, \ + you won't be able to sign any transactions." + .to_string(), + ); + } + }); + + // Second column: the text field + ui.text_edit_singleline(key); + + // Third column: the remove button + if ui.button("-").clicked() { + keys_to_remove.push(i); + } + + ui.end_row(); + } + + // Remove the keys after the loop to avoid borrowing conflicts + for i in keys_to_remove.iter().rev() { + self.keys_input.remove(*i); + } + } + } + }); + ui.add_space(10.0); // Add button to add more keys - if ui.button("+ Add Key").clicked() { + if ui.button("+ Add key manually").clicked() { self.keys_input.push(String::new()); } ui.add_space(10.0); From d4c1b9efe8bd3a19c3b2757e7d4abf24c1a498ed Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Tue, 28 Oct 2025 16:32:43 +0700 Subject: [PATCH 6/9] cleanup ui --- src/ui/identities/add_existing_identity_screen.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/identities/add_existing_identity_screen.rs b/src/ui/identities/add_existing_identity_screen.rs index 7639f7c8e..8399c119f 100644 --- a/src/ui/identities/add_existing_identity_screen.rs +++ b/src/ui/identities/add_existing_identity_screen.rs @@ -258,7 +258,7 @@ impl AddExistingIdentityScreen { } else { ui.colored_label( Color32::GRAY, - "No wallets are currently loaded. Unlock or import one to scan for keys.", + "No wallets are currently loaded. Import one to scan for keys.", ); } } @@ -463,7 +463,7 @@ impl AddExistingIdentityScreen { if wallets_len == 0 { ui.colored_label( Color32::GRAY, - "No wallets available. Import or create a wallet to search by derivation path.", + "No wallets available. Import a wallet to search by derivation path.", ); return action; } From f7e470ffef7675e02431679d7f54aef48e073c63 Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Tue, 28 Oct 2025 16:38:54 +0700 Subject: [PATCH 7/9] set max index search to 30 --- src/backend_task/identity/load_identity.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend_task/identity/load_identity.rs b/src/backend_task/identity/load_identity.rs index 5bafc1b72..eb1d75416 100644 --- a/src/backend_task/identity/load_identity.rs +++ b/src/backend_task/identity/load_identity.rs @@ -32,7 +32,7 @@ use std::collections::BTreeMap; use std::convert::TryInto; use std::sync::{Arc, RwLock}; -const MAX_IDENTITY_INDEX_SEARCH: u32 = 200; +const MAX_IDENTITY_INDEX_SEARCH: u32 = 30; type WalletKeyMap = BTreeMap<(PrivateKeyTarget, u32), (QualifiedIdentityPublicKey, PrivateKeyData)>; type WalletMatchResult = Option<(WalletSeedHash, u32, WalletKeyMap)>; From 938e3c5ea2bf17455e7216512030cb4764506e7b Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Tue, 28 Oct 2025 16:41:25 +0700 Subject: [PATCH 8/9] set max const --- src/backend_task/identity/load_identity.rs | 5 ++--- src/ui/identities/add_new_identity_screen/mod.rs | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/backend_task/identity/load_identity.rs b/src/backend_task/identity/load_identity.rs index eb1d75416..c5f63dd43 100644 --- a/src/backend_task/identity/load_identity.rs +++ b/src/backend_task/identity/load_identity.rs @@ -12,6 +12,7 @@ use crate::model::qualified_identity::{ DPNSNameInfo, IdentityStatus, IdentityType, QualifiedIdentity, }; use crate::model::wallet::{Wallet, WalletSeedHash}; +use crate::ui::identities::add_new_identity_screen::MAX_IDENTITY_INDEX; use dash_sdk::Sdk; use dash_sdk::dashcore_rpc::dashcore::PrivateKey; use dash_sdk::dashcore_rpc::dashcore::key::Secp256k1; @@ -32,8 +33,6 @@ use std::collections::BTreeMap; use std::convert::TryInto; use std::sync::{Arc, RwLock}; -const MAX_IDENTITY_INDEX_SEARCH: u32 = 30; - type WalletKeyMap = BTreeMap<(PrivateKeyTarget, u32), (QualifiedIdentityPublicKey, PrivateKeyData)>; type WalletMatchResult = Option<(WalletSeedHash, u32, WalletKeyMap)>; @@ -434,7 +433,7 @@ impl AppContext { } } - for candidate_index in 0..=MAX_IDENTITY_INDEX_SEARCH { + for candidate_index in 0..MAX_IDENTITY_INDEX { let (public_key_map, public_key_hash_map) = wallet .identity_authentication_ecdsa_public_keys_data_map( self.network, diff --git a/src/ui/identities/add_new_identity_screen/mod.rs b/src/ui/identities/add_new_identity_screen/mod.rs index 040b1a540..833b40dbd 100644 --- a/src/ui/identities/add_new_identity_screen/mod.rs +++ b/src/ui/identities/add_new_identity_screen/mod.rs @@ -34,6 +34,8 @@ use std::fmt; use std::sync::atomic::Ordering; use std::sync::{Arc, RwLock}; +pub const MAX_IDENTITY_INDEX: u32 = 30; + #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum FundingMethod { NoSelection, @@ -240,8 +242,8 @@ impl AddNewIdentityScreen { ComboBox::from_id_salt("identity_index") .selected_text(selected_text) .show_ui(ui, |ui| { - // Provide up to 30 entries for selection (0 to 29) - for i in 0..30 { + // Provide up to 30 entries for selection + for i in 0..MAX_IDENTITY_INDEX { let is_used = used_indices.contains(&i); let label = if is_used { format!("{} (used)", i) From 133ef5d5198208f316ffa66061603d79f9166615 Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Tue, 28 Oct 2025 16:48:25 +0700 Subject: [PATCH 9/9] k --- src/backend_task/identity/load_identity_from_wallet.rs | 4 ++-- src/ui/identities/add_existing_identity_screen.rs | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/backend_task/identity/load_identity_from_wallet.rs b/src/backend_task/identity/load_identity_from_wallet.rs index 4d1b589c4..0acf3ae15 100644 --- a/src/backend_task/identity/load_identity_from_wallet.rs +++ b/src/backend_task/identity/load_identity_from_wallet.rs @@ -55,8 +55,8 @@ impl AppContext { sender .send(TaskResult::Success(Box::new( BackendTaskSuccessResult::Message(format!( - "Searching for identity using key at index {}...", - key_index + "Searching for identity at index {} using key at index {}...", + identity_index, key_index )), ))) .await diff --git a/src/ui/identities/add_existing_identity_screen.rs b/src/ui/identities/add_existing_identity_screen.rs index 8399c119f..b1db1db2f 100644 --- a/src/ui/identities/add_existing_identity_screen.rs +++ b/src/ui/identities/add_existing_identity_screen.rs @@ -512,7 +512,9 @@ impl AddExistingIdentityScreen { let identity_index_label = match self.wallet_search_mode { WalletIdentitySearchMode::SpecificIndex => "Identity index:", - WalletIdentitySearchMode::UpToIndex => "Highest identity index to search (inclusive):", + WalletIdentitySearchMode::UpToIndex => { + "Highest identity index to search (inclusive, max 29):" + } }; ui.horizontal(|ui| {