Skip to content

Commit 75fbec7

Browse files
committed
feat(multipath): Add unit tests for multipath utils
1 parent 2424425 commit 75fbec7

1 file changed

Lines changed: 79 additions & 5 deletions

File tree

src/utils.rs

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,9 @@ where
241241
let ext_descriptor = wallet_opts.ext_descriptor.clone();
242242
let int_descriptor = wallet_opts.int_descriptor.clone();
243243

244-
let is_multipath = ext_descriptor.contains('<') && ext_descriptor.contains(';');
245-
if is_multipath && int_descriptor.is_some() {
244+
if is_multipath_desc(&ext_descriptor) && int_descriptor.is_some() {
246245
return Err(Error::AmbiguousDescriptors);
247-
}
246+
};
248247

249248
let mut wallet_load_params = Wallet::load();
250249
wallet_load_params =
@@ -288,8 +287,7 @@ pub(crate) fn new_wallet(network: Network, wallet_opts: &WalletOpts) -> Result<W
288287
let ext_descriptor = wallet_opts.ext_descriptor.clone();
289288
let int_descriptor = wallet_opts.int_descriptor.clone();
290289

291-
let is_multipath = ext_descriptor.contains('<') && ext_descriptor.contains(';');
292-
if is_multipath && int_descriptor.is_some() {
290+
if is_multipath_desc(&ext_descriptor) && int_descriptor.is_some() {
293291
return Err(Error::AmbiguousDescriptors);
294292
}
295293

@@ -658,3 +656,79 @@ pub fn load_wallet_config(
658656

659657
Ok((wallet_opts, network))
660658
}
659+
660+
/// Helper to check if a descriptor string contains a BIP389 multipath expression.
661+
fn is_multipath_desc(desc_str: &str) -> bool {
662+
let desc_str = desc_str.split('#').next().unwrap_or(desc_str).trim();
663+
664+
desc_str.contains('<') && desc_str.contains(';') && desc_str.contains('>')
665+
}
666+
667+
#[cfg(test)]
668+
mod tests {
669+
use super::*;
670+
use crate::commands::WalletOpts;
671+
use bdk_wallet::{bitcoin::Network, rusqlite::Connection};
672+
673+
#[test]
674+
fn test_is_multipath_descriptor() {
675+
let multipath_desc = "wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/<0;1>/*)";
676+
let desc = "wpkh([07234a14/84'/1'/0']tpubDCSgT6PaVLQH9h2TAxKryhvkEurUBcYRJc9dhTcMDyahhWiMWfEWvQQX89yaw7w7XU8bcVujoALfxq59VkFATri3Cxm5mkp9kfHfRFDckEh/0/*)#429nsxmg";
677+
let multi_path = is_multipath_desc(multipath_desc);
678+
let result = is_multipath_desc(desc);
679+
assert!(multi_path);
680+
assert!(!result);
681+
}
682+
683+
#[test]
684+
fn test_multipath_detection_and_initialization() {
685+
let network = Network::Testnet;
686+
let multipath_desc = "wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/<0;1>/*)";
687+
let mut db = Connection::open_in_memory().expect("should open in memory db");
688+
689+
let opts = WalletOpts {
690+
ext_descriptor: multipath_desc.to_string(),
691+
int_descriptor: None,
692+
wallet: Some("test_wallet".to_string()),
693+
verbose: false,
694+
database_type: crate::commands::DatabaseType::Sqlite,
695+
};
696+
697+
let result = new_persisted_wallet(network, &mut db, &opts);
698+
assert!(result.is_ok(), "Multipath initialization should succeed");
699+
700+
let wallet = result.unwrap();
701+
let ext_desc = wallet.public_descriptor(KeychainKind::External).to_string();
702+
let int_desc = wallet.public_descriptor(KeychainKind::Internal).to_string();
703+
704+
assert!(ext_desc.contains("/0/*"), "External should use index 0");
705+
assert!(int_desc.contains("/1/*"), "Internal should use index 1");
706+
707+
assert!(ext_desc.contains("9a6a2580"));
708+
assert!(int_desc.contains("9a6a2580"));
709+
}
710+
711+
#[test]
712+
fn test_error_on_ambiguous_descriptors() {
713+
let network = Network::Testnet;
714+
let multipath_desc = "wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/<0;1>/*)";
715+
let internal_desc = "wpkh([07234a14/84'/1'/0']tpubDCSgT6PaVLQH9h2TAxKryhvkEurUBcYRJc9dhTcMDyahhWiMWfEWvQQX89yaw7w7XU8bcVujoALfxq59VkFATri3Cxm5mkp9kfHfRFDckEh/1/*)#y7qjdnts";
716+
717+
let mut db = Connection::open_in_memory().expect("should open in memory db");
718+
719+
let opts = WalletOpts {
720+
ext_descriptor: multipath_desc.to_string(),
721+
int_descriptor: Some(internal_desc.to_string()),
722+
wallet: Some("test_wallet".to_string()),
723+
verbose: true,
724+
database_type: crate::commands::DatabaseType::Sqlite,
725+
};
726+
727+
let result = new_persisted_wallet(network, &mut db, &opts);
728+
729+
match result {
730+
Err(Error::AmbiguousDescriptors) => (),
731+
_ => panic!("Should have returned AmbiguousDescriptors error"),
732+
}
733+
}
734+
}

0 commit comments

Comments
 (0)