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
8 changes: 8 additions & 0 deletions src/context/wallet_lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,14 @@ impl AppContext {
}
}

if derivation_path.is_bip32() {
return (DerivationPathReference::BIP32, default_type);
}

if derivation_path.is_bip44(self.network) {
return (DerivationPathReference::BIP44, default_type);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

(default_ref, default_type)
}

Expand Down
20 changes: 12 additions & 8 deletions src/model/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,20 @@ pub trait DerivationPathHelpers {
) -> DerivationPath;
}

pub(crate) fn is_bip44_path(path: &DerivationPath, network: Network) -> bool {
let coin_type = match network {
Network::Dash => 5,
_ => 1,
};
let components = path.as_ref();
components.len() >= 4
&& components[0] == ChildNumber::Hardened { index: 44 }
&& components[1] == ChildNumber::Hardened { index: coin_type }
}

impl DerivationPathHelpers for DerivationPath {
fn is_bip44(&self, network: Network) -> bool {
let coin_type = match network {
Network::Dash => 5,
_ => 1,
};
let components = self.as_ref();
components.len() >= 4
&& components[0] == ChildNumber::Hardened { index: 44 }
&& components[1] == ChildNumber::Hardened { index: coin_type }
is_bip44_path(self, network)
}

fn is_bip44_external(&self, network: Network) -> bool {
Expand Down
96 changes: 87 additions & 9 deletions src/ui/wallets/account_summary.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::collections::BTreeMap;

use dash_sdk::dpp::balances::credits::Credits;
use dash_sdk::dpp::dashcore::Network;
use dash_sdk::dpp::key_wallet::bip32::DerivationPath;

use crate::model::wallet::{DerivationPathHelpers, DerivationPathReference, Wallet};

Expand Down Expand Up @@ -51,9 +53,10 @@ impl AccountCategory {

pub fn label(&self, index: Option<u32>) -> String {
match self {
AccountCategory::Bip44 => match index.unwrap_or(0) {
0 => "Main Account".to_string(),
idx => format!("BIP44 Account #{}", idx),
AccountCategory::Bip44 => match index {
Some(0) => "Main Account".to_string(),
Some(idx) => format!("BIP44 Account #{}", idx),
None => "BIP44 Account".to_string(),
},
AccountCategory::Bip32 => match index {
Some(idx) if idx > 0 => format!("Legacy BIP32 Account #{}", idx),
Expand Down Expand Up @@ -150,6 +153,29 @@ impl AccountCategory {
}
}

pub(crate) fn categorize_account_path(
path: &DerivationPath,
network: Network,
reference: DerivationPathReference,
) -> (AccountCategory, Option<u32>) {
// Derivation path shape is authoritative over stored metadata.
// This prevents stale/misclassified references from surfacing wrong account labels.
let category = if path.is_bip32() {
AccountCategory::Bip32
} else if path.is_bip44(network) {
AccountCategory::Bip44
} else {
AccountCategory::from_reference(reference)
};

let index = match category {
AccountCategory::Bip44 | AccountCategory::Bip32 => path.bip44_account_index(),
_ => None,
};

(category, index)
}

#[derive(Clone, Debug)]
pub struct AccountSummary {
pub category: AccountCategory,
Expand Down Expand Up @@ -199,15 +225,11 @@ impl AccountSummaryBuilder {
}
}

pub fn collect_account_summaries(wallet: &Wallet) -> Vec<AccountSummary> {
pub fn collect_account_summaries(wallet: &Wallet, network: Network) -> Vec<AccountSummary> {
let mut builders: BTreeMap<AccountKey, AccountSummaryBuilder> = BTreeMap::new();

for (path, info) in &wallet.watched_addresses {
let category = AccountCategory::from_reference(info.path_reference);
let index = match category {
AccountCategory::Bip44 | AccountCategory::Bip32 => path.bip44_account_index(),
_ => None,
};
let (category, index) = categorize_account_path(path, network, info.path_reference);

let balance = wallet
.address_balances
Expand Down Expand Up @@ -243,3 +265,59 @@ pub fn collect_account_summaries(wallet: &Wallet) -> Vec<AccountSummary> {

summaries
}

#[cfg(test)]
mod tests {
use super::*;
use dash_sdk::dpp::key_wallet::bip32::ChildNumber;

#[test]
fn bip44_without_account_index_is_not_main_account() {
assert_eq!(AccountCategory::Bip44.label(None), "BIP44 Account");
}

#[test]
fn legacy_path_overrides_incorrect_bip44_reference() {
let path = DerivationPath::from(vec![
ChildNumber::Hardened { index: 0 },
ChildNumber::Normal { index: 1 },
ChildNumber::Normal { index: 3 },
]);

let (category, index) =
categorize_account_path(&path, Network::Testnet, DerivationPathReference::BIP44);
assert_eq!(category, AccountCategory::Bip32);
assert_eq!(index, None);
}

#[test]
fn bip44_path_overrides_incorrect_bip32_reference() {
let path = DerivationPath::from(vec![
ChildNumber::Hardened { index: 44 },
ChildNumber::Hardened { index: 1 },
ChildNumber::Hardened { index: 0 },
ChildNumber::Normal { index: 0 },
ChildNumber::Normal { index: 1 },
]);

let (category, index) =
categorize_account_path(&path, Network::Testnet, DerivationPathReference::BIP32);
assert_eq!(category, AccountCategory::Bip44);
assert_eq!(index, Some(0));
}

#[test]
fn bip44_requires_matching_coin_type_for_network() {
let path = DerivationPath::from(vec![
ChildNumber::Hardened { index: 44 },
ChildNumber::Hardened { index: 5 },
ChildNumber::Hardened { index: 0 },
ChildNumber::Normal { index: 0 },
ChildNumber::Normal { index: 1 },
]);

let (category, _) =
categorize_account_path(&path, Network::Testnet, DerivationPathReference::Unknown);
assert_ne!(category, AccountCategory::Bip44);
}
}
17 changes: 8 additions & 9 deletions src/ui/wallets/wallets_screen/address_table.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::app::AppAction;
use crate::model::wallet::{DerivationPathHelpers, DerivationPathReference};
use crate::ui::wallets::account_summary::AccountCategory;
use crate::ui::wallets::account_summary::{AccountCategory, categorize_account_path};
use crate::ui::{MessageType, ScreenLike};
use dash_sdk::dashcore_rpc::dashcore::{Address, Network};
use dash_sdk::dpp::balances::credits::CREDITS_PER_DUFF;
Expand Down Expand Up @@ -93,13 +93,9 @@ impl WalletsBalancesScreen {
pub(super) fn categorize_path(
path: &DerivationPath,
reference: DerivationPathReference,
network: Network,
) -> (AccountCategory, Option<u32>) {
let category = AccountCategory::from_reference(reference);
let index = match category {
AccountCategory::Bip44 | AccountCategory::Bip32 => path.bip44_account_index(),
_ => None,
};
(category, index)
categorize_account_path(path, network, reference)
}

pub(super) fn render_address_table(&mut self, ui: &mut Ui) -> AppAction {
Expand Down Expand Up @@ -153,8 +149,11 @@ impl WalletsBalancesScreen {
.get(derivation_path)
.map(|info| info.path_reference)
.unwrap_or(DerivationPathReference::Unknown);
let (account_category, account_index) =
Self::categorize_path(derivation_path, path_reference);
let (account_category, account_index) = Self::categorize_path(
derivation_path,
path_reference,
self.app_context.network,
);

// Get Platform credits balance for Platform Payment addresses
// Use canonical lookup to handle potential Address key mismatches
Expand Down
4 changes: 2 additions & 2 deletions src/ui/wallets/wallets_screen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ impl WalletsBalancesScreen {
.selected_account
.as_ref()
.is_some_and(|(category, index)| {
*category == AccountCategory::Bip44 && index.unwrap_or(0) == 0
*category == AccountCategory::Bip44 && *index == Some(0)
});

if wallet_is_open && is_main_account {
Expand Down Expand Up @@ -1133,7 +1133,7 @@ impl WalletsBalancesScreen {
let summaries = {
let wallet = wallet_arc.read().unwrap();
self.render_wallet_overview(ui, &wallet);
collect_account_summaries(&wallet)
collect_account_summaries(&wallet, self.app_context.network)
};

self.ensure_account_selection(&summaries);
Expand Down
Loading