]> Untitled Git - bdk-cli/commitdiff
fix descriptors generation
authorVihiga Tyonum <withtvpeter@gmail.com>
Wed, 24 Sep 2025 13:57:33 +0000 (14:57 +0100)
committerVihiga Tyonum <withtvpeter@gmail.com>
Mon, 3 Nov 2025 10:43:40 +0000 (11:43 +0100)
src/commands.rs
src/error.rs
src/handlers.rs
src/utils.rs

index 09cbc5dd342ac2d04a0e120e8de2885527b9ee52..6983d6de9ccce4f41706dbc80ab48fce95afe987 100644 (file)
@@ -17,7 +17,11 @@ use bdk_wallet::bitcoin::{
     Address, Network, OutPoint, ScriptBuf,
     bip32::{DerivationPath, Xpriv},
 };
-use clap::{Args, Parser, Subcommand, ValueEnum, builder::TypedValueParser, value_parser};
+use clap::{
+    Args, Parser, Subcommand, ValueEnum,
+    builder::{PossibleValuesParser, TypedValueParser},
+    value_parser,
+};
 
 #[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc"))]
 use crate::utils::parse_proxy_auth;
@@ -488,7 +492,7 @@ pub enum DescriptorSubCommand {
         #[arg(
             long = "type",
             short = 't',
-            value_parser = clap::builder::PossibleValuesParser::new(["44", "49", "84", "86"])
+            value_parser = PossibleValuesParser::new(["44", "49", "84", "86"])
                 .map(|s| s.parse::<u8>().unwrap()),
             default_value = "84"
         )]
@@ -499,7 +503,4 @@ pub enum DescriptorSubCommand {
         /// Optional key input
         key: Option<String>,
     },
-
-    /// Show info about a given descriptor
-    Info { descriptor: String },
 }
index 3916f26161c8be22666507df6f2f475a14b8854b..1b8b5b456a9f6d1525a0b95c3b26a65ea1146653 100644 (file)
@@ -5,8 +5,8 @@ use thiserror::Error;
 
 #[derive(Debug, Error)]
 pub enum BDKCliError {
-    #[error("BIP39 error: {0}")]
-    BIP39Error(#[from] bdk_wallet::bip39::Error),
+    #[error("BIP39 error: {0:?}")]
+    BIP39Error(#[from] Option<bdk_wallet::bip39::Error>),
 
     #[error("BIP32 error: {0}")]
     BIP32Error(#[from] bdk_wallet::bitcoin::bip32::Error),
@@ -103,30 +103,6 @@ pub enum BDKCliError {
     #[cfg(feature = "cbf")]
     #[error("BDK-Kyoto update error: {0}")]
     KyotoUpdateError(#[from] bdk_kyoto::UpdateError),
-
-    #[error("Mnemonic generation failed: {0}")]
-    MnemonicGenerationError(String),
-
-    #[error("Xpriv creation failed: {0}")]
-    XprivCreationError(String),
-
-    #[error("Descriptor parsing failed: {0}")]
-    DescriptorParsingError(String),
-
-    #[error("Invalid extended key (xpub): {0}")]
-    InvalidKey(String),
-
-    #[error("Invalid derivation path: {0}")]
-    InvalidDerivationPath(String),
-
-    #[error("Unsupported script type: {0}")]
-    UnsupportedScriptType(u8),
-
-    #[error("Descriptor key conversion failed: {0}")]
-    DescriptorKeyError(String),
-
-    #[error("Invalid arguments: {0}")]
-    InvalidArguments(String),
 }
 
 impl From<ExtractTxError> for BDKCliError {
index 9264e47737deac9ab46fb69dc84fac2aad4c1fc8..3787eebdff75e285fa99493362608c27c684143d 100644 (file)
@@ -15,8 +15,6 @@ use crate::commands::*;
 use crate::error::BDKCliError as Error;
 #[cfg(any(feature = "sqlite", feature = "redb"))]
 use crate::persister::Persister;
-#[cfg(feature = "cbf")]
-use crate::utils::BlockchainClient::KyotoClient;
 use crate::utils::*;
 #[cfg(feature = "redb")]
 use bdk_redb::Store as RedbStore;
@@ -35,27 +33,25 @@ use bdk_wallet::keys::{
     bip39::WordCount,
 };
 use bdk_wallet::miniscript::miniscript;
-use bdk_wallet::bitcoin::bip32::{DerivationPath, KeySource};
-use bdk_wallet::bitcoin::consensus::encode::serialize_hex;
-use bdk_wallet::bitcoin::script::PushBytesBuf;
-use bdk_wallet::bitcoin::Network;
-use bdk_wallet::bitcoin::{secp256k1::Secp256k1, Txid};
-use bdk_wallet::bitcoin::{Amount, FeeRate, Psbt, Sequence};
-use bdk_wallet::descriptor::{Descriptor, Segwitv0};
-use bdk_wallet::keys::bip39::WordCount;
 #[cfg(feature = "sqlite")]
 use bdk_wallet::rusqlite::Connection;
 use bdk_wallet::{KeychainKind, SignOptions, Wallet};
 #[cfg(feature = "compiler")]
 use bdk_wallet::{
+    bitcoin::XOnlyPublicKey,
     descriptor::{Descriptor, Legacy, Miniscript},
-    miniscript::{Tap, descriptor::TapTree, policy::Concrete},
     descriptor::{Legacy, Miniscript},
     miniscript::policy::Concrete,
+    miniscript::{Tap, descriptor::TapTree, policy::Concrete},
 };
 use cli_table::{Cell, CellStruct, Style, Table, format::Justify};
 use serde_json::json;
-use bdk_wallet::{KeychainKind, SignOptions, Wallet};
+#[cfg(feature = "cbf")]
+use {
+    crate::utils::BlockchainClient::KyotoClient,
+    bdk_kyoto::{Info, LightClient},
+    tokio::select,
+};
 
 #[cfg(feature = "electrum")]
 use crate::utils::BlockchainClient::Electrum;
@@ -64,29 +60,16 @@ use bdk_kyoto::LightClient;
 #[cfg(feature = "compiler")]
 use bdk_wallet::bitcoin::XOnlyPublicKey;
 use bdk_wallet::bitcoin::base64::prelude::*;
-use bdk_wallet::keys::DescriptorKey::Secret;
-use bdk_wallet::keys::{
-    DerivableKey, DescriptorKey, DescriptorKey::Secret, DescriptorPublicKey, ExtendedKey,
-    GeneratableKey, GeneratedKey, bip39::WordCount,
-};
-use bdk_wallet::miniscript::miniscript;
-use serde_json::{Value, json};
+use serde_json::Value;
 use std::collections::BTreeMap;
 #[cfg(any(feature = "electrum", feature = "esplora"))]
 use std::collections::HashSet;
 use std::convert::TryFrom;
-use std::fmt;
 #[cfg(any(feature = "repl", feature = "electrum", feature = "esplora"))]
 use std::io::Write;
 use std::str::FromStr;
-
-#[cfg(feature = "electrum")]
-use crate::utils::BlockchainClient::Electrum;
-#[cfg(feature = "cbf")]
-use bdk_kyoto::{Info, LightClient};
-use bdk_wallet::bitcoin::base64::prelude::*;
-#[cfg(feature = "cbf")]
-use tokio::select;
+#[cfg(any(feature = "redb", feature = "compiler"))]
+use std::sync::Arc;
 #[cfg(any(
     feature = "electrum",
     feature = "esplora",
@@ -95,7 +78,7 @@ use tokio::select;
 ))]
 use {
     crate::commands::OnlineWalletSubCommand::*,
-    bdk_wallet::bitcoin::{consensus::Decodable, hex::FromHex, Transaction},
+    bdk_wallet::bitcoin::{Transaction, consensus::Decodable, hex::FromHex},
 };
 #[cfg(feature = "esplora")]
 use {crate::utils::BlockchainClient::Esplora, bdk_esplora::EsploraAsyncExt};
@@ -1287,8 +1270,7 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
             subcommand: descriptor_subcommand,
         } => {
             let network = cli_opts.network;
-            let descriptor = handle_descriptor_subcommand(network, descriptor_subcommand)
-                .map_err(|e| Error::Generic(e.to_string()))?;
+            let descriptor = handle_descriptor_subcommand(network, descriptor_subcommand)?;
             let json = serde_json::to_string_pretty(&descriptor)?;
             Ok(json)
         }
@@ -1375,84 +1357,35 @@ pub fn handle_descriptor_subcommand(
             multipath,
             key,
         } => {
-            let (descriptor_type, derivation_path_str) = match r#type {
-                44 => (DescriptorType::Bip44, "m/44h/1h/0h"),
-                49 => (DescriptorType::Bip49, "m/49h/1h/0h"),
-                84 => (DescriptorType::Bip84, "m/84h/1h/0h"),
-                86 => (DescriptorType::Bip86, "m/86h/1h/0h"),
-                _ => return Err(Error::UnsupportedScriptType(r#type)),
+            let descriptor_type = match r#type {
+                44 => DescriptorType::Bip44,
+                49 => DescriptorType::Bip49,
+                84 => DescriptorType::Bip84,
+                86 => DescriptorType::Bip86,
+                _ => {
+                    return Err(Error::Generic(
+                        "Unsupported script type: {r#type}".to_string(),
+                    ));
+                }
             };
 
             match (multipath, key.as_ref()) {
-                (true, Some(k)) => generate_multipath_descriptor(&network, r#type, k),
+                // generate multipath descriptors
+                (true, Some(k)) => generate_descriptors(&network, descriptor_type, k, true),
                 (false, Some(k)) => {
                     if is_mnemonic(k) {
-                        generate_descriptor_from_mnemonic_string(
-                            k,
-                            network,
-                            derivation_path_str,
-                            descriptor_type,
-                        )
+                        // generate descriptors from given mnemonic string
+                        generate_descriptor_from_mnemonic_string(k, network, descriptor_type)
                     } else {
-                        generate_standard_descriptor(&network, r#type, k)
+                        // generate descriptors from key
+                        generate_descriptors(&network, descriptor_type, k, false)
                     }
                 }
+                // generate mnemonic and descriptors
                 (false, None) => generate_new_descriptor_with_mnemonic(network, descriptor_type),
-                _ => Err(Error::InvalidArguments(
-                    "Provide a key or weak string".to_string(),
-                )),
+                _ => Err(Error::Generic("Provide a key or string".to_string())),
             }
         }
-        DescriptorSubCommand::Info { descriptor } => {
-            let parsed: Descriptor<DescriptorPublicKey> = descriptor
-                .parse()
-                .map_err(|e| Error::Generic(format!("Failed to parse descriptor: {}", e)))?;
-
-            let checksum = parsed.to_string();
-            let script_type = match parsed {
-                Descriptor::Wpkh(_) => "wpkh",
-                Descriptor::Pkh(_) => "pkh",
-                Descriptor::Sh(_) => "sh",
-                Descriptor::Tr(_) => "tr",
-                _ => "other",
-            };
-
-            let json = json!({
-                "descriptor": checksum,
-                "type": script_type,
-                "is_multipath": descriptor.contains("/*"),
-            });
-
-            Ok(json)
-        }
-    }
-}
-
-pub fn generate_standard_descriptor(
-    network: &Network,
-    script_type: u8,
-    key: &str,
-) -> Result<Value, Error> {
-    let descriptor_type = match script_type {
-        44 => DescriptorType::Bip44,
-        49 => DescriptorType::Bip49,
-        84 => DescriptorType::Bip84,
-        86 => DescriptorType::Bip86,
-        _ => return Err(Error::UnsupportedScriptType(script_type)),
-    };
-
-    generate_descriptor_from_key_by_type(network, key, descriptor_type)
-}
-
-impl fmt::Display for DescriptorType {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let s = match self {
-            DescriptorType::Bip44 => "bip44",
-            DescriptorType::Bip49 => "bip49",
-            DescriptorType::Bip84 => "bip84",
-            DescriptorType::Bip86 => "bip86",
-        };
-        write!(f, "{}", s)
     }
 }
 
index 93392ba5ae7f441cb84f039c51fcac8afb4e4aee..3a4d2cb7dfffafb6c2673821c10127b2542e9b17 100644 (file)
 //!
 //! This module includes all the utility tools used by the App.
 use crate::error::BDKCliError as Error;
-use std::{fmt::Display, path::{Path, PathBuf}, str::FromStr};
-use std::str::FromStr;
-
-use std::path::{Path, PathBuf};
+use std::{
+    fmt::{Display, Formatter},
+    path::{Path, PathBuf},
+    str::FromStr,
+};
 
 use crate::commands::WalletOpts;
 #[cfg(feature = "cbf")]
@@ -22,6 +23,7 @@ use bdk_kyoto::{
     builder::Builder,
 };
 use bdk_wallet::bitcoin::{Address, Network, OutPoint, ScriptBuf};
+use bdk_wallet::miniscript::Legacy;
 
 #[cfg(any(
     feature = "electrum",
@@ -36,7 +38,7 @@ use bdk_wallet::Wallet;
 use bdk_wallet::{KeychainKind, PersistedWallet, WalletPersister};
 
 use bdk_wallet::bip39::{Language, Mnemonic};
-use bdk_wallet::bitcoin::bip32::ChildNumber;
+use bdk_wallet::bitcoin::bip32::Fingerprint;
 use bdk_wallet::bitcoin::{
     bip32::{DerivationPath, Xpriv, Xpub},
     secp256k1::Secp256k1,
@@ -378,161 +380,49 @@ pub async fn sync_kyoto_client(wallet: &mut Wallet, client: Box<LightClient>) ->
     Ok(())
 }
 
-pub(crate) fn shorten(displayable: impl Display, start: u8, end: u8) -> String {
-    let displayable = displayable.to_string();
-    let start_str: &str = &displayable[0..start as usize];
-    let end_str: &str = &displayable[displayable.len() - end as usize..];
-    format!("{start_str}...{end_str}")
-pub fn generate_descriptor_from_key_by_type(
-    network: &Network,
-    key: &str,
-    descriptor_type: DescriptorType,
-) -> Result<serde_json::Value, Error> {
-    let derivation_path = match descriptor_type {
-        DescriptorType::Bip44 => "m/44h/1h/0h",
-        DescriptorType::Bip49 => "m/49h/1h/0h",
-        DescriptorType::Bip84 => "m/84h/1h/0h",
-        DescriptorType::Bip86 => "m/86h/1h/0h",
-    };
-
-    generate_bip_descriptor_from_key(network, key, derivation_path, descriptor_type)
-}
-
-pub fn generate_new_descriptor_with_mnemonic(
-    network: Network,
-    descriptor_type: DescriptorType,
-) -> Result<serde_json::Value, Error> {
-    let secp = Secp256k1::new();
-
-    // Generate a new BIP39 mnemonic
-    let mnemonic: GeneratedKey<Mnemonic, Segwitv0> =
-        Mnemonic::generate((WordCount::Words12, Language::English)).map_err(|e| {
-            Error::MnemonicGenerationError(format!("Mnemonic generation failed: {:?}", e))
-        })?;
-
-    let seed = mnemonic.to_seed("");
-    let xprv =
-        Xpriv::new_master(network, &seed).map_err(|e| Error::XprivCreationError(e.to_string()))?;
-
-    let origin = xprv.fingerprint(&secp);
-
-    let (derivation_base, external_fmt, internal_fmt) = match descriptor_type {
-        DescriptorType::Bip44 => ("/44h/1h/0h", "pkh", "pkh"),
-        DescriptorType::Bip49 => ("/49h/1h/0h", "sh(wpkh", "sh(wpkh"),
-        DescriptorType::Bip84 => ("/84h/1h/0h", "wpkh", "wpkh"),
-        DescriptorType::Bip86 => ("/86h/1h/0h", "tr", "tr"),
-    };
-    let path = DerivationPath::from_str(&format!("m{}", derivation_base))
-        .map_err(|e| Error::Generic(e.to_string()))?;
-
-    let derived_xprv = xprv
-        .derive_priv(&secp, &path)
-        .map_err(|e| Error::Generic(e.to_string()))?;
-
-    let xprv_str = derived_xprv.to_string();
-
-    // Construct descriptors
-    let external_desc = match descriptor_type {
-        DescriptorType::Bip49 => format!(
-            "{}([{}{}]{}{}))",
-            external_fmt, origin, derivation_base, xprv_str, "/0"
-        ),
-        _ => format!(
-            "{}([{}{}]{}{})",
-            external_fmt, origin, derivation_base, xprv_str, "/0"
-        ),
-    };
-
-    let internal_desc = match descriptor_type {
-        DescriptorType::Bip49 => format!(
-            "{}([{}{}]{}{}))",
-            internal_fmt, origin, derivation_base, xprv_str, "/1"
-        ),
-        _ => format!(
-            "{}([{}{}]{}{})",
-            internal_fmt, origin, derivation_base, xprv_str, "/1"
-        ),
-    };
-
-    // Parse descriptors
-    let (ext_desc, ext_keymap) =
-        Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &external_desc)
-            .map_err(|e| Error::DescriptorParsingError(e.to_string()))?;
-
-    let (int_desc, int_keymap) =
-        Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &internal_desc).map_err(
-            |e| {
-                Error::DescriptorParsingError(format!("Failed to parse internal descriptor: {}", e))
-            },
-        )?;
-
-    Ok(serde_json::json!({
-        "type": descriptor_type.to_string(),
-        "mnemonic": mnemonic.to_string(),
-        "private_descriptors": {
-            "external": ext_desc.to_string_with_secret(&ext_keymap),
-            "internal": int_desc.to_string_with_secret(&int_keymap),
-        },
-        "public_descriptors": {
-            "external": ext_desc.to_string(),
-            "internal": int_desc.to_string(),
-        }
-    }))
+pub fn is_mnemonic(s: &str) -> bool {
+    let word_count = s.split_whitespace().count();
+    (12..=24).contains(&word_count) && s.chars().all(|c| c.is_alphanumeric() || c.is_whitespace())
 }
 
-pub fn generate_multipath_descriptor(
+pub fn generate_descriptors(
     network: &Network,
-    script_type: u8,
+    descriptor_type: DescriptorType,
     key: &str,
+    multipath_label: bool,
 ) -> Result<Value, Error> {
-    use DescriptorType::*;
-
-    let descriptor_type = match script_type {
-        44 => Bip44,
-        49 => Bip49,
-        84 => Bip84,
-        86 => Bip86,
-        _ => return Err(Error::UnsupportedScriptType(script_type)),
-    };
-
     type DescriptorConstructor =
         fn(DescriptorPublicKey) -> Result<Descriptor<DescriptorPublicKey>, Error>;
 
-    let (derivation_base, descriptor_constructor): (&str, DescriptorConstructor) =
-        match descriptor_type {
-            Bip44 => ("/44h/1h/0h", |key| {
-                Descriptor::new_pkh(key).map_err(Error::from)
-            }),
-            Bip49 => ("/49h/1h/0h", |key| {
-                Descriptor::new_sh_wpkh(key).map_err(Error::from)
-            }),
-            Bip84 => ("/84h/1h/0h", |key| {
-                Descriptor::new_wpkh(key).map_err(Error::from)
-            }),
-            Bip86 => ("/86h/1h/0h", |key| {
-                Descriptor::new_tr(key, None).map_err(Error::from)
-            }),
-        };
+    let purpose = match descriptor_type {
+        DescriptorType::Bip44 => 44,
+        DescriptorType::Bip49 => 49,
+        DescriptorType::Bip84 => 84,
+        DescriptorType::Bip86 => 86,
+    };
+
+    let derivation_base = format!("/{purpose}h/1h/0h");
+
+    let descriptor_constructor: DescriptorConstructor = match descriptor_type {
+        DescriptorType::Bip44 => |key| Descriptor::new_pkh(key).map_err(Error::from),
+        DescriptorType::Bip49 => |key| Descriptor::new_sh_wpkh(key).map_err(Error::from),
+        DescriptorType::Bip84 => |key| Descriptor::new_wpkh(key).map_err(Error::from),
+        DescriptorType::Bip86 => |key| Descriptor::new_tr(key, None).map_err(Error::from),
+    };
 
     let secp = Secp256k1::new();
-    let derivation_path = DerivationPath::from_str(&format!("m{}", derivation_base))
-        .map_err(|e| Error::InvalidDerivationPath(e.to_string()))?;
+    let derivation_path = DerivationPath::from_str(&format!("m{derivation_base}"))?;
 
-    // Determine if it's an xprv or xpub
     let is_private = key.starts_with("xprv") || key.starts_with("tprv");
 
-    // Use xprv or xpub accordingly
     type DescriptorBuilderFn = Box<dyn Fn(u32) -> Result<(String, Option<String>), Error>>;
 
-    let (fingerprint, make_desc): (_, DescriptorBuilderFn) = if is_private {
-        let xprv: Xpriv = key
-            .parse()
-            .map_err(|e| Error::InvalidKey(format!("Invalid xprv: {e}")))?;
+    let (fingerprint, make_desc): (Fingerprint, DescriptorBuilderFn) = if is_private {
+        let xprv: Xpriv = key.parse()?;
         let fingerprint = xprv.fingerprint(&secp);
 
         let closure = move |change: u32| -> Result<(String, Option<String>), Error> {
-            let branch_path = DerivationPath::from_str(&change.to_string())
-                .map_err(|e| Error::InvalidDerivationPath(e.to_string()))?;
+            let branch_path = DerivationPath::from_str(&change.to_string())?;
 
             let desc_xprv = DescriptorXKey {
                 origin: Some((fingerprint, derivation_path.clone())),
@@ -543,37 +433,32 @@ pub fn generate_multipath_descriptor(
 
             let desc_secret = DescriptorSecretKey::XPrv(desc_xprv.clone());
             let (desc_key, keymap, _) = match descriptor_type {
-                DescriptorType::Bip84 | DescriptorType::Bip49 | DescriptorType::Bip44 => {
-                    IntoDescriptorKey::<Segwitv0>::into_descriptor_key(desc_secret)
-                        .map_err(|e| Error::DescriptorKeyError(e.to_string()))?
-                        .extract(&secp)
-                        .map_err(|e| Error::DescriptorKeyError(e.to_string()))?
+                DescriptorType::Bip44 => {
+                    IntoDescriptorKey::<Legacy>::into_descriptor_key(desc_secret)?.extract(&secp)?
+                }
+                DescriptorType::Bip84 | DescriptorType::Bip49 => {
+                    IntoDescriptorKey::<Segwitv0>::into_descriptor_key(desc_secret)?
+                        .extract(&secp)?
+                }
+                DescriptorType::Bip86 => {
+                    IntoDescriptorKey::<Tap>::into_descriptor_key(desc_secret)?.extract(&secp)?
                 }
-                DescriptorType::Bip86 => IntoDescriptorKey::<Tap>::into_descriptor_key(desc_secret)
-                    .map_err(|e| Error::DescriptorKeyError(e.to_string()))?
-                    .extract(&secp)
-                    .map_err(|e| Error::DescriptorKeyError(e.to_string()))?,
             };
 
-            let public_descriptor = descriptor_constructor(desc_key.clone())?;
-            let private_descriptor = descriptor_constructor(desc_key)?;
-
+            let descriptor = descriptor_constructor(desc_key)?;
             Ok((
-                public_descriptor.to_string(),
-                Some(private_descriptor.to_string_with_secret(&keymap)),
+                descriptor.to_string(),
+                Some(descriptor.to_string_with_secret(&keymap)),
             ))
         };
 
         (fingerprint, Box::new(closure))
     } else {
-        let xpub: Xpub = key
-            .parse()
-            .map_err(|e| Error::InvalidKey(format!("Invalid xpub: {e}")))?;
+        let xpub: Xpub = key.parse()?;
         let fingerprint = xpub.fingerprint();
 
         let closure = move |change: u32| -> Result<(String, Option<String>), Error> {
-            let branch_path = DerivationPath::from_str(&change.to_string())
-                .map_err(|e| Error::InvalidDerivationPath(e.to_string()))?;
+            let branch_path = DerivationPath::from_str(&change.to_string())?;
 
             let desc_xpub = DescriptorXKey {
                 origin: Some((fingerprint, derivation_path.clone())),
@@ -590,12 +475,17 @@ pub fn generate_multipath_descriptor(
         (fingerprint, Box::new(closure))
     };
 
-    // Build descriptors
     let (external_pub, external_priv) = make_desc(0)?;
     let (internal_pub, internal_priv) = make_desc(1)?;
 
+    let type_label = if multipath_label {
+        format!("{descriptor_type}-multipath")
+    } else {
+        descriptor_type.to_string()
+    };
+
     let mut result = json!({
-        "type": format!("{}-multipath", descriptor_type),
+        "type": type_label,
         "public_descriptors": {
             "external": external_pub,
             "internal": internal_pub
@@ -614,118 +504,37 @@ pub fn generate_multipath_descriptor(
     Ok(result)
 }
 
-pub fn generate_bip_descriptor_from_key(
-    network: &Network,
-    key: &str,
-    derivation_path_str: &str,
+pub fn generate_new_descriptor_with_mnemonic(
+    network: Network,
     descriptor_type: DescriptorType,
 ) -> Result<serde_json::Value, Error> {
-    let secp = Secp256k1::new();
-
-    let derivation_path: DerivationPath = derivation_path_str
-        .parse()
-        .map_err(|e| Error::InvalidDerivationPath(format!("DerivationPath Error: {e}")))?;
-
-    let xprv: Xpriv = key
-        .parse()
-        .map_err(|e| Error::InvalidKey(format!("Invalid xprv: {e}")))?;
-
-    let fingerprint = xprv.fingerprint(&secp);
-
-    let make_desc_key = |branch: u32| -> Result<(String, String), Error> {
-        let branch_path = DerivationPath::from(vec![ChildNumber::Normal { index: branch }]);
-
-        let desc_xprv = DescriptorXKey {
-            origin: Some((fingerprint, derivation_path.clone())),
-            xkey: xprv,
-            derivation_path: branch_path.clone(),
-            wildcard: Wildcard::Unhardened,
-        };
-
-        let desc_secret = DescriptorSecretKey::XPrv(desc_xprv.clone());
-
-        let (desc_key, keymap, _) =
-            IntoDescriptorKey::<Segwitv0>::into_descriptor_key(desc_secret.clone())
-                .map_err(|e| Error::DescriptorKeyError(e.to_string()))?
-                .extract(&secp)
-                .map_err(|e| Error::DescriptorKeyError(e.to_string()))?;
-
-        let public_descriptor = match descriptor_type {
-            DescriptorType::Bip84 => Descriptor::new_wpkh(desc_key.clone())?,
-            DescriptorType::Bip86 => Descriptor::new_tr(desc_key.clone(), None)?,
-            DescriptorType::Bip49 => Descriptor::new_sh_wpkh(desc_key.clone())?,
-            DescriptorType::Bip44 => Descriptor::new_pkh(desc_key.clone())?,
-        };
-
-        let private_descriptor = match descriptor_type {
-            DescriptorType::Bip84 => Descriptor::new_wpkh(desc_key)?,
-            DescriptorType::Bip86 => Descriptor::new_tr(desc_key, None)?,
-            DescriptorType::Bip49 => Descriptor::new_sh_wpkh(desc_key)?,
-            DescriptorType::Bip44 => Descriptor::new_pkh(desc_key)?,
-        };
-
-        Ok((
-            public_descriptor.to_string(),
-            private_descriptor.to_string_with_secret(&keymap),
-        ))
-    };
+    let mnemonic: GeneratedKey<Mnemonic, Segwitv0> =
+        Mnemonic::generate((WordCount::Words12, Language::English)).map_err(Error::BIP39Error)?;
 
-    let (external_pub, external_priv) = make_desc_key(0)?;
-    let (internal_pub, internal_priv) = make_desc_key(1)?;
+    let seed = mnemonic.to_seed("");
+    let xprv = Xpriv::new_master(network, &seed)?;
 
-    Ok(json!({
-        "type": descriptor_type.to_string(),
-        "fingerprint": fingerprint.to_string(),
-        "network": network.to_string(),
-        "private_descriptors": {
-            "external": external_priv,
-            "internal": internal_priv
-        },
-        "public_descriptors": {
-            "external": external_pub,
-            "internal": internal_pub
-        }
-    }))
+    let mut result = generate_descriptors(&network, descriptor_type, &xprv.to_string(), false)?;
+    result["mnemonic"] = json!(mnemonic.to_string());
+    Ok(result)
 }
 
 pub fn generate_descriptor_from_mnemonic_string(
     mnemonic_str: &str,
     network: Network,
-    derivation_path_str: &str,
     descriptor_type: DescriptorType,
 ) -> Result<serde_json::Value, Error> {
-    let secp = Secp256k1::new();
-
-    let mnemonic = Mnemonic::parse_in(Language::English, mnemonic_str)
-        .map_err(|e| Error::Generic(e.to_string()))?;
-    let ext_key: ExtendedKey = mnemonic
-        .into_extended_key()
-        .map_err(|e| Error::Generic(e.to_string()))?;
+    let mnemonic = Mnemonic::parse_in(Language::English, mnemonic_str)?;
+    let ext_key: ExtendedKey = mnemonic.into_extended_key()?;
     let xprv = ext_key
         .into_xprv(network)
         .ok_or_else(|| Error::Generic("No xprv found".to_string()))?;
 
-    let _fingerprint = xprv.fingerprint(&secp);
-    let derivation_path: DerivationPath = derivation_path_str
-        .parse()
-        .map_err(|e| Error::InvalidDerivationPath(format!("DerivationPath Error: {e}")))?;
-
-    let xprv = xprv
-        .derive_priv(&secp, &derivation_path)
-        .map_err(|e| Error::InvalidKey(format!("Failed to derive xprv: {e}")))?;
-
-    generate_bip_descriptor_from_key(
-        &network,
-        &xprv.to_string(),
-        derivation_path_str,
-        descriptor_type,
-    )
+    let mut result = generate_descriptors(&network, descriptor_type, &xprv.to_string(), false)?;
+    result["mnemonic"] = json!(mnemonic_str);
+    Ok(result)
 }
 
-pub fn is_mnemonic(s: &str) -> bool {
-    let word_count = s.split_whitespace().count();
-    (12..=24).contains(&word_count) && s.chars().all(|c| c.is_alphanumeric() || c.is_whitespace())
-}
 // Enum for descriptor types
 #[derive(Debug, Clone, Copy)]
 pub enum DescriptorType {
@@ -734,3 +543,22 @@ pub enum DescriptorType {
     Bip84,
     Bip86,
 }
+
+impl Display for DescriptorType {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        let s = match self {
+            DescriptorType::Bip44 => "bip44",
+            DescriptorType::Bip49 => "bip49",
+            DescriptorType::Bip84 => "bip84",
+            DescriptorType::Bip86 => "bip86",
+        };
+        write!(f, "{s}")
+    }
+}
+
+pub(crate) fn shorten(displayable: impl Display, start: u8, end: u8) -> String {
+    let displayable = displayable.to_string();
+    let start_str: &str = &displayable[0..start as usize];
+    let end_str: &str = &displayable[displayable.len() - end as usize..];
+    format!("{start_str}...{end_str}")
+}