From: Vihiga Tyonum Date: Wed, 29 Jul 2026 22:24:19 +0000 (+0100) Subject: feat(multipath): limit multipath desc to two paths X-Git-Url: http://internal-gitweb-vhost/java/src/database/struct.CommandStringError.html?a=commitdiff_plain;h=697fe0b1eb97b8b461ec5096697bbb66b8bbeae1;p=bdk-cli feat(multipath): limit multipath desc to two paths - Add a check for a two-paths multipath descriptors as supported by the wallet api. --- diff --git a/src/utils/descriptors.rs b/src/utils/descriptors.rs index d6c5e40..924041d 100644 --- a/src/utils/descriptors.rs +++ b/src/utils/descriptors.rs @@ -199,14 +199,26 @@ pub fn generate_descriptor_from_mnemonic( Ok(result) } -/// Returns `true` if `descriptor` is a BIP-389 multipath descriptor (e.g. `.../<0;1>/*`). +/// Returns `true` if `descriptor` is a supported two-path BIP-389 multipath descriptor +/// (external and internal), `false` for a normal single-path descriptor. /// -/// Parses via IntoWalletDescriptor and uses miniscript's `is_multipath`. -/// Returns an error if the descriptor is unparseable. +/// Errors if the descriptor is unparseable, or if it's a multipath descriptor with a number +/// of paths other than two (supports only external/internal two-path multipath). pub fn is_multipath_descriptor(descriptor: &str, network: Network) -> Result { let secp = Secp256k1::new(); let (descriptor, _) = descriptor.into_wallet_descriptor(&secp, network.into())?; - Ok(descriptor.is_multipath()) + + if !descriptor.is_multipath() { + return Ok(false); + } + + let paths = descriptor.into_single_descriptors()?.len(); + if paths != 2 { + return Err(Error::Generic(format!( + "Unsupported multipath descriptor: expected exactly 2 paths (external/internal), found {paths}." + ))); + } + Ok(true) } #[cfg(test)] @@ -214,6 +226,7 @@ mod multipath_tests { use super::*; const MULTIPATH: &str = "wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/<0;1>/*)"; + const THREE_PATH: &str = "wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/<0;1;2>/*)"; const SINGLE: &str = "wpkh([07234a14/84'/1'/0']tpubDCSgT6PaVLQH9h2TAxKryhvkEurUBcYRJc9dhTcMDyahhWiMWfEWvQQX89yaw7w7XU8bcVujoALfxq59VkFATri3Cxm5mkp9kfHfRFDckEh/0/*)#429nsxmg"; #[test] @@ -230,4 +243,9 @@ mod multipath_tests { fn rejects_unparseable() { assert!(is_multipath_descriptor("not a descriptor", Network::Testnet).is_err()); } + + #[test] + fn rejects_more_than_two_paths() { + assert!(is_multipath_descriptor(THREE_PATH, Network::Testnet).is_err()); + } }