]> Untitled Git - bdk-cli/commitdiff
refactor: replace bdk::error with custom error
authorVihiga Tyonum <withtvpeter@gmail.com>
Thu, 6 Feb 2025 12:14:32 +0000 (13:14 +0100)
committerVihiga Tyonum <withtvpeter@gmail.com>
Thu, 6 Feb 2025 14:03:23 +0000 (15:03 +0100)
- replace bdk::error with custom error enum
- update imports and handling of errors

[Ticket: X]

Cargo.lock
Cargo.toml
src/commands.rs
src/handlers.rs
src/main.rs
src/utils.rs
src/wasm.rs

index 69e03606b7e3886b306ab67b217eff77ca39c1ab..ae97e2dca4f1752041b58261c566fe58c81e576f 100644 (file)
@@ -209,7 +209,6 @@ dependencies = [
  "clap",
  "dirs-next",
  "electrsd",
- "electrum-client 0.23.0",
  "env_logger",
  "fd-lock",
  "getrandom 0.2.10",
@@ -893,23 +892,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "electrum-client"
-version = "0.23.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8ed4d35bb98a55540bb5b735731486febddf9cc9b6e96f5b3fd2536eed81a4e"
-dependencies = [
- "bitcoin 0.32.5",
- "byteorder",
- "libc",
- "log",
- "rustls 0.23.22",
- "serde",
- "serde_json",
- "webpki-roots 0.25.4",
- "winapi",
-]
-
 [[package]]
 name = "encoding_rs"
 version = "0.8.32"
index 35caf5937d51a673eca2e1228aff8aa944bef0a7..c7a3e9cb07d93e9fc94c4fbd7f39372d8e5eed32 100644 (file)
@@ -34,8 +34,6 @@ bdk_electrum ={ version = "0.20.1", optional = true}
 bdk_esplora ={ version = "0.20.1", features = ["async"], optional = true}
 bdk_bitcoind_rpc = {version = "0.17.1", optional = true}
 hwi = {version = "0.10.0", optional = true}
-electrum-client = "0.23.0"
-
 
 # Platform-specific dependencies
 [target.'cfg(target_arch = "wasm32")'.dependencies]
index a80b8101f4d792169241dce2816f1626861e777b..707c3cf5021d130bede5f2fba8de0b20a69e345b 100644 (file)
@@ -1041,7 +1041,7 @@ mod test {
                 subcommand: WalletSubCommand::OfflineWalletSubCommand(CreateTx {
                     recipients: vec![(script1, 123456), (script2, 78910)],
                     send_all: false,
-                    enable_rbf: false,
+                    enable_rbf: true,
                     offline_signer: false,
                     utxos: Some(vec!(outpoint1, outpoint2)),
                     unspendable: None,
index 37efff874335a7ea5f42c75f890f5ae59ed6dde2..e4ed88c4c74811b8fbb87a2f582a1f2ebd99fdf3 100644 (file)
@@ -22,47 +22,44 @@ use crate::commands::OfflineWalletSubCommand::*;
 ))]
 use crate::commands::OnlineWalletSubCommand::*;
 use crate::commands::*;
+use crate::error::BDKCliError as Error;
 use crate::utils::*;
-use bdk::Error;
 use bdk_wallet::bip39::{Language, Mnemonic};
 use bdk_wallet::bitcoin::script::PushBytesBuf;
 use bdk_wallet::bitcoin::{Amount, FeeRate, Psbt, Sequence};
 use bdk_wallet::rusqlite::Connection;
 
 use bdk_wallet::keys::bip39::WordCount;
-use bdk_wallet::{KeychainKind, PersistedWallet};
+use bdk_wallet::{KeychainKind, PersistedWallet, SignOptions};
 use clap::Parser;
 
-use bdk_wallet::bitcoin::bip32::{DerivationPath, KeySource};
-use bdk_wallet::bitcoin::consensus::encode::{serialize, serialize_hex};
 #[cfg(any(
     feature = "electrum",
     feature = "esplora",
     feature = "compact_filters",
     feature = "rpc"
 ))]
-use bdk_wallet::bitcoin::hashes::hex::FromHex;
+use bdk_reserves::bdk::{
+    blockchain::{log_progress, Blockchain},
+    SyncOptions,
+};
+use bdk_wallet::bitcoin::bip32::{DerivationPath, KeySource};
+use bdk_wallet::bitcoin::consensus::encode::{serialize, serialize_hex};
 use bdk_wallet::bitcoin::{secp256k1::Secp256k1, Network, Txid};
-#[cfg(any(
-    feature = "electrum",
-    feature = "esplora",
-    feature = "compact_filters",
-    feature = "rpc"
-))]
-use bdk_wallet::blockchain::{log_progress, Blockchain};
 use bdk_wallet::descriptor::Segwitv0;
 #[cfg(feature = "compiler")]
 use bdk_wallet::descriptor::{Descriptor, Legacy, Miniscript};
 #[cfg(all(feature = "reserves", feature = "electrum"))]
-use bdk_wallet::electrum_client::{Client, ElectrumApi};
-#[cfg(feature = "hardware-signer")]
-use bdk_wallet::hwi::{
-    interface::HWIClient,
-    types::{HWIChain, HWIDescriptor},
-};
+use electrum_client::{Client, ElectrumApi};
+
 use bdk_wallet::keys::DescriptorKey::Secret;
 use bdk_wallet::keys::{DerivableKey, DescriptorKey, ExtendedKey, GeneratableKey, GeneratedKey};
 use bdk_wallet::miniscript::miniscript;
+#[cfg(feature = "hardware-signer")]
+use hwi::{
+    types::{HWIChain, HWIDescriptor},
+    HWIClient,
+};
 
 use bdk_macros::maybe_async;
 #[cfg(any(
@@ -72,23 +69,16 @@ use bdk_macros::maybe_async;
     feature = "rpc"
 ))]
 use bdk_macros::maybe_await;
+#[cfg(all(feature = "reserves", feature = "electrum"))]
+use bdk_reserves::bdk::{bitcoin::Address, blockchain::Capability};
 #[cfg(feature = "reserves")]
-use bdk_reserves::reserves::verify_proof;
-#[cfg(feature = "reserves")]
-use bdk_reserves::reserves::ProofOfReserves;
+use bdk_reserves::reserves::{verify_proof, ProofOfReserves};
 #[cfg(feature = "compiler")]
 use bdk_wallet::miniscript::policy::Concrete;
-#[cfg(feature = "hardware-signer")]
-use bdk_wallet::wallet::signer::SignerError;
-use bdk_wallet::SignOptions;
-#[cfg(all(feature = "reserves", feature = "electrum"))]
-use bdk_wallet::{bitcoin::Address, blockchain::Capability};
 #[cfg(feature = "repl")]
 use regex::Regex;
 #[cfg(feature = "repl")]
-use rustyline::error::ReadlineError;
-#[cfg(feature = "repl")]
-use rustyline::Editor;
+use rustyline::{error::ReadlineError, Editor};
 use serde_json::json;
 use std::str::FromStr;
 
@@ -197,14 +187,11 @@ pub fn handle_offline_wallet_subcommand(
             ];
 
             for (policy, keychain) in policies.into_iter().flatten() {
-                let policy = serde_json::from_str::<BTreeMap<String, Vec<usize>>>(&policy)
-                    .map_err(|s| Error::Generic(s.to_string()))?;
+                let policy = serde_json::from_str::<BTreeMap<String, Vec<usize>>>(&policy)?;
                 tx_builder.policy_path(policy, keychain);
             }
 
-            let psbt = tx_builder
-                .finish()
-                .map_err(|e| Error::Generic(e.to_string()))?;
+            let psbt = tx_builder.finish()?;
 
             let serialized_psbt = psbt.serialize();
             let psbt_base64 = base64::encode(&serialized_psbt);
@@ -225,11 +212,9 @@ pub fn handle_offline_wallet_subcommand(
             unspendable,
             fee_rate,
         } => {
-            let txid = Txid::from_str(txid.as_str()).map_err(|s| Error::Generic(s.to_string()))?;
+            let txid = Txid::from_str(txid.as_str())?;
 
-            let mut tx_builder = wallet
-                .build_fee_bump(txid)
-                .map_err(|e| Error::Generic(e.to_string()))?;
+            let mut tx_builder = wallet.build_fee_bump(txid)?;
             let fee_rate =
                 FeeRate::from_sat_per_vb(fee_rate as u64).unwrap_or(FeeRate::BROADCAST_MIN);
             tx_builder.fee_rate(fee_rate);
@@ -251,9 +236,7 @@ pub fn handle_offline_wallet_subcommand(
                 tx_builder.unspendable(unspendable);
             }
 
-            let psbt = tx_builder
-                .finish()
-                .map_err(|e| Error::Generic(e.to_string()))?;
+            let psbt = tx_builder.finish()?;
 
             let serialized_psbt = psbt.serialize();
             let psbt_base64 = base64::encode(serialized_psbt);
@@ -261,12 +244,8 @@ pub fn handle_offline_wallet_subcommand(
             Ok(json!({"psbt": psbt_base64, "details": psbt}))
         }
         Policies => {
-            let external_policy = wallet
-                .policies(KeychainKind::External)
-                .map_err(|e| Error::Generic(e.to_string()))?;
-            let internal_policy = wallet
-                .policies(KeychainKind::Internal)
-                .map_err(|e| Error::Generic(e.to_string()))?;
+            let external_policy = wallet.policies(KeychainKind::External)?;
+            let internal_policy = wallet.policies(KeychainKind::Internal)?;
 
             Ok(json!({
                 "external": external_policy,
@@ -282,17 +261,14 @@ pub fn handle_offline_wallet_subcommand(
             assume_height,
             trust_witness_utxo,
         } => {
-            let psbt_bytes = base64::decode(psbt).map_err(|e| Error::Generic(e.to_string()))?;
-            let mut psbt =
-                Psbt::deserialize(&psbt_bytes).map_err(|e| Error::Generic(e.to_string()))?;
+            let psbt_bytes = base64::decode(psbt)?;
+            let mut psbt = Psbt::deserialize(&psbt_bytes)?;
             let signopt = SignOptions {
                 assume_height,
                 trust_witness_utxo: trust_witness_utxo.unwrap_or(false),
                 ..Default::default()
             };
-            let finalized = wallet
-                .sign(&mut psbt, signopt)
-                .map_err(|e| Error::Generic(e.to_string()))?;
+            let finalized = wallet.sign(&mut psbt, signopt)?;
             if wallet_opts.verbose {
                 Ok(
                     json!({"psbt": base64::encode(serialize(&psbt_bytes)),"is_finalized": finalized, "serialized_psbt": psbt}),
@@ -304,13 +280,9 @@ pub fn handle_offline_wallet_subcommand(
             }
         }
         ExtractPsbt { psbt } => {
-            let psbt_serialized =
-                base64::decode(psbt).map_err(|e| Error::Generic(e.to_string()))?;
-            let psbt =
-                Psbt::deserialize(&psbt_serialized).map_err(|e| Error::Generic(e.to_string()))?;
-            let raw_tx = psbt
-                .extract_tx()
-                .map_err(|e| Error::Generic(e.to_string()))?;
+            let psbt_serialized = base64::decode(psbt)?;
+            let psbt = Psbt::deserialize(&psbt_serialized)?;
+            let raw_tx = psbt.extract_tx()?;
             Ok(json!({"raw_tx": serialize_hex(&raw_tx),}))
         }
         FinalizePsbt {
@@ -318,18 +290,15 @@ pub fn handle_offline_wallet_subcommand(
             assume_height,
             trust_witness_utxo,
         } => {
-            let psbt_bytes = base64::decode(psbt).map_err(|e| Error::Generic(e.to_string()))?;
-            let mut psbt: Psbt =
-                Psbt::deserialize(&psbt_bytes).map_err(|e| Error::Generic(e.to_string()))?;
+            let psbt_bytes = base64::decode(psbt)?;
+            let mut psbt: Psbt = Psbt::deserialize(&psbt_bytes)?;
 
             let signopt = SignOptions {
                 assume_height,
                 trust_witness_utxo: trust_witness_utxo.unwrap_or(false),
                 ..Default::default()
             };
-            let finalized = wallet
-                .finalize_psbt(&mut psbt, signopt)
-                .map_err(|e| Error::Generic(e.to_string()))?;
+            let finalized = wallet.finalize_psbt(&mut psbt, signopt)?;
             if wallet_opts.verbose {
                 Ok(
                     json!({ "psbt": base64::encode(serialize(&psbt_bytes)),"is_finalized": finalized, "serialized_psbt": psbt}),
@@ -344,10 +313,8 @@ pub fn handle_offline_wallet_subcommand(
             let mut psbts = psbt
                 .iter()
                 .map(|s| {
-                    let psbt = base64::decode(s).map_err(|e| Error::Generic(e.to_string()))?;
-                    let psbt: Psbt =
-                        Psbt::deserialize(&psbt).map_err(|e| Error::Generic(e.to_string()))?;
-                    Ok(psbt)
+                    let psbt = base64::decode(s)?;
+                    Ok(Psbt::deserialize(&psbt)?)
                 })
                 .collect::<Result<Vec<_>, Error>>()?;
 
@@ -376,17 +343,11 @@ pub fn handle_offline_wallet_subcommand(
     feature = "compact_filters",
     feature = "rpc"
 ))]
-pub(crate) fn handle_online_wallet_subcommand<B, D>(
-    wallet: &Wallet<D>,
+pub(crate) fn handle_online_wallet_subcommand(
+    wallet: &Wallet,
     blockchain: &B,
     online_subcommand: OnlineWalletSubCommand,
-) -> Result<serde_json::Value, Error>
-where
-    B: Blockchain,
-    D: BatchDatabase,
-{
-    use bdk::SyncOptions;
-
+) -> Result<serde_json::Value, Error> {
     match online_subcommand {
         Sync => {
             maybe_await!(wallet.sync(
@@ -401,11 +362,11 @@ where
             let tx = match (psbt, tx) {
                 (Some(psbt), None) => {
                     let psbt = base64::decode(&psbt).map_err(|e| Error::Generic(e.to_string()))?;
-                    let psbt: PartiallySignedTransaction = deserialize(&psbt)?;
+                    let psbt: Psbt = Psbt::deserialize(&psbt)?;
                     is_final(&psbt)?;
                     psbt.extract_tx()
                 }
-                (None, Some(tx)) => deserialize(&Vec::<u8>::from_hex(&tx)?)?,
+                (None, Some(tx)) => Psbt::deserialize(&Vec::<u8>::from_hex(&tx)?)?,
                 (Some(_), Some(_)) => panic!("Both `psbt` and `tx` options not allowed"),
                 (None, None) => panic!("Missing `psbt` and `tx` option"),
             };
@@ -435,8 +396,8 @@ where
             msg,
             confirmations,
         } => {
-            let psbt = base64::decode(&psbt).unwrap();
-            let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap();
+            let psbt = base64::decode(&psbt)?;
+            let psbt: Psbt = Psbt::deserialize(&psbt)?;
             let current_height = blockchain.get_height()?;
             let max_confirmation_height = if confirmations == 0 {
                 None
@@ -467,7 +428,7 @@ where
     feature = "compact_filters",
     feature = "rpc"
 ))]
-pub(crate) fn is_final(psbt: &PartiallySignedTransaction) -> Result<(), Error> {
+pub(crate) fn is_final(psbt: &Psbt) -> Result<(), Error> {
     let unsigned_tx_inputs = psbt.unsigned_tx.input.len();
     let psbt_inputs = psbt.inputs.len();
     if unsigned_tx_inputs != psbt_inputs {
@@ -513,9 +474,7 @@ pub(crate) fn handle_key_subcommand(
                 Mnemonic::generate((mnemonic_type, Language::English))
                     .map_err(|_| Error::Generic("Mnemonic generation error".to_string()))?;
             let mnemonic = mnemonic.into_key();
-            let xkey: ExtendedKey = (mnemonic.clone(), password)
-                .into_extended_key()
-                .map_err(|e| Error::Generic(format!("Extended key generation error: {:?}", e)))?;
+            let xkey: ExtendedKey = (mnemonic.clone(), password).into_extended_key()?;
             let xprv = xkey.into_xprv(network).ok_or_else(|| {
                 Error::Generic("Privatekey info not found (should not happen)".to_string())
             })?;
@@ -530,11 +489,8 @@ pub(crate) fn handle_key_subcommand(
             )
         }
         KeySubCommand::Restore { mnemonic, password } => {
-            let mnemonic = Mnemonic::parse_in(Language::English, mnemonic)
-                .map_err(|e| Error::Generic(e.to_string()))?;
-            let xkey: ExtendedKey = (mnemonic, password)
-                .into_extended_key()
-                .map_err(|e| Error::Generic(format!("Extended key generation error: {:?}", e)))?;
+            let mnemonic = Mnemonic::parse_in(Language::English, mnemonic)?;
+            let xkey: ExtendedKey = (mnemonic, password).into_extended_key()?;
             let xprv = xkey.into_xprv(network).ok_or_else(|| {
                 Error::Generic("Privatekey info not found (should not happen)".to_string())
             })?;
@@ -544,27 +500,20 @@ pub(crate) fn handle_key_subcommand(
         }
         KeySubCommand::Derive { xprv, path } => {
             if xprv.network != network.into() {
-                return Err(Error::Key(bdk::keys::KeyError::InvalidNetwork));
+                return Err(Error::Generic("Invalid network".to_string()));
             }
-            let derived_xprv = &xprv
-                .derive_priv(&secp, &path)
-                .map_err(|e| Error::Generic(e.to_string()))?;
+            let derived_xprv = &xprv.derive_priv(&secp, &path)?;
 
             let origin: KeySource = (xprv.fingerprint(&secp), path);
 
-            let derived_xprv_desc_key: DescriptorKey<Segwitv0> = derived_xprv
-                .into_descriptor_key(Some(origin), DerivationPath::default())
-                .map_err(|e| Error::Generic(e.to_string()))?;
+            let derived_xprv_desc_key: DescriptorKey<Segwitv0> =
+                derived_xprv.into_descriptor_key(Some(origin), DerivationPath::default())?;
 
             if let Secret(desc_seckey, _, _) = derived_xprv_desc_key {
-                let desc_pubkey = desc_seckey
-                    .to_public(&secp)
-                    .map_err(|e| Error::Generic(e.to_string()))?;
+                let desc_pubkey = desc_seckey.to_public(&secp)?;
                 Ok(json!({"xpub": desc_pubkey.to_string(), "xprv": desc_seckey.to_string()}))
             } else {
-                Err(Error::Key(bdk::keys::KeyError::Message(
-                    "Invalid key variant".to_string(),
-                )))
+                Err(Error::Generic("Invalid key variant".to_string()))
             }
         }
         #[cfg(feature = "hardware-signer")]
@@ -574,15 +523,14 @@ pub(crate) fn handle_key_subcommand(
                 Network::Testnet => HWIChain::Test,
                 Network::Regtest => HWIChain::Regtest,
                 Network::Signet => HWIChain::Signet,
+                Network::Testnet4 => HWIChain::Test,
+                _ => Err(Error::Generic("Invalid network".to_string()))?,
             };
-            let devices = HWIClient::enumerate().map_err(|e| SignerError::from(e))?;
+            let devices = HWIClient::enumerate()?;
             let descriptors = devices.iter().map(|device_| {
-                let device = device_.clone()
-                    .map_err(|e| SignerError::from(e))?;
-                let client = HWIClient::get_client(&device, true, chain.clone())
-                    .map_err(|e| SignerError::from(e))?;
-                let descriptors: HWIDescriptor<String> = client.get_descriptors(None)
-                    .map_err(|e| SignerError::from(e))?;
+                let device = device_.clone().map_err(|e| Error::Generic(e.to_string()))?;
+                let client = HWIClient::get_client(&device, true, chain.clone())?;
+                let descriptors: HWIDescriptor<String> = client.get_descriptors(None)?;
                 Ok(json!({"device": device.model, "receiving": descriptors.receive[0].to_string(), "change": descriptors.internal[0]}))
             }).collect::<Result<Vec<_>, Error>>()?;
             Ok(json!(descriptors))
@@ -612,8 +560,7 @@ pub(crate) fn handle_compile_subcommand(
         "wsh" => Descriptor::new_wsh(segwit_policy),
         "sh-wsh" => Descriptor::new_sh_wsh(segwit_policy),
         _ => panic!("Invalid type"),
-    }
-    .map_err(Error::Miniscript)?;
+    }?;
 
     Ok(json!({"descriptor": descriptor.to_string()}))
 }
@@ -628,11 +575,10 @@ pub(crate) fn handle_ext_reserves_subcommand(
     psbt: String,
     confirmations: usize,
     addresses: Vec<String>,
-    electrum_opts: ElectrumOpts,
+    electrum_opts: ElectrumApi,
 ) -> Result<serde_json::Value, Error> {
-    let psbt = base64::decode(&psbt)
-        .map_err(|e| Error::Generic(format!("Base64 decode error: {:?}", e)))?;
-    let psbt: PartiallySignedTransaction = deserialize(&psbt)?;
+    let psbt = base64::decode(&psbt)?;
+    let psbt: Psbt = Psbt::deserialize(&psbt)?;
     let client = Client::new(&electrum_opts.server)?;
 
     let current_block_height = client.block_headers_subscribe().map(|data| data.height)?;
@@ -641,8 +587,7 @@ pub(crate) fn handle_ext_reserves_subcommand(
     let outpoints_per_addr = addresses
         .iter()
         .map(|address| {
-            let address = Address::from_str(address)
-                .map_err(|e| Error::Generic(format!("Invalid address: {:?}", e)))?;
+            let address = Address::from_str(address)?;
             get_outpoints_for_address(address, &client, max_confirmation_height)
         })
         .collect::<Result<Vec<Vec<_>>, Error>>()?;
@@ -730,8 +675,7 @@ pub(crate) fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
             //     println!("No previous history.");
             // }
 
-            let split_regex = Regex::new(crate::REPL_LINE_SPLIT_REGEX)
-                .map_err(|e| Error::Generic(e.to_string()))?;
+            let split_regex = Regex::new(crate::REPL_LINE_SPLIT_REGEX)?;
 
             #[cfg(any(
                 feature = "electrum",
@@ -855,19 +799,20 @@ pub(crate) fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
 ))]
 #[cfg(test)]
 mod test {
+    use bdk_wallet::bitcoin::Psbt;
+
     use super::is_final;
-    use bdk::bitcoin::psbt::PartiallySignedTransaction;
     use std::str::FromStr;
 
     #[test]
     fn test_psbt_is_final() {
-        let unsigned_psbt = PartiallySignedTransaction::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOwEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEAACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap();
+        let unsigned_psbt = Psbt::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOwEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEAACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap();
         assert!(is_final(&unsigned_psbt).is_err());
 
-        let part_signed_psbt = PartiallySignedTransaction::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOyICA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDSDBFAiEAnNPpu6wNX2HXYz8s2q5nXug4cWfvCGD3SSH2CNKm+yECIEQO7/URhUPsGoknMTE+GrYJf9Wxqn9QsuN9FGj32cQpAQEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEAACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap();
+        let part_signed_psbt = Psbt::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOyICA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDSDBFAiEAnNPpu6wNX2HXYz8s2q5nXug4cWfvCGD3SSH2CNKm+yECIEQO7/URhUPsGoknMTE+GrYJf9Wxqn9QsuN9FGj32cQpAQEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEAACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap();
         assert!(is_final(&part_signed_psbt).is_err());
 
-        let full_signed_psbt = PartiallySignedTransaction::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOwEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEBBwABCNsEAEgwRQIhAJzT6busDV9h12M/LNquZ17oOHFn7whg90kh9gjSpvshAiBEDu/1EYVD7BqJJzExPhq2CX/Vsap/ULLjfRRo99nEKQFHMEQCIGoFCvJ2zPB7PCpznh4+1jsY03kMie49KPoPDdr7/T9TAiB3jV7wzR9BH11FSbi+8U8gSX95PrBlnp1lOBgTUIUw3QFHUiED8lXZT/Sldb6I/j1ByxiKUS+RkR3imGYMzydXzAL4x4MhAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJUq4AACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap();
+        let full_signed_psbt = Psbt::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOwEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEBBwABCNsEAEgwRQIhAJzT6busDV9h12M/LNquZ17oOHFn7whg90kh9gjSpvshAiBEDu/1EYVD7BqJJzExPhq2CX/Vsap/ULLjfRRo99nEKQFHMEQCIGoFCvJ2zPB7PCpznh4+1jsY03kMie49KPoPDdr7/T9TAiB3jV7wzR9BH11FSbi+8U8gSX95PrBlnp1lOBgTUIUw3QFHUiED8lXZT/Sldb6I/j1ByxiKUS+RkR3imGYMzydXzAL4x4MhAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJUq4AACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap();
         assert!(is_final(&full_signed_psbt).is_ok());
     }
 }
index 18708c01e3584a52776990cad78d134f41d9b64e..a3b82d17b45fcdd244a15c28319e3d47e1fb09ac 100644 (file)
@@ -11,6 +11,7 @@
 #![warn(missing_docs)]
 
 mod commands;
+mod error;
 mod handlers;
 mod nodes;
 mod utils;
@@ -22,8 +23,8 @@ use bitcoin::Network;
 use log::{debug, error, warn};
 
 use crate::commands::CliOpts;
+use crate::error::BDKCliError as Error;
 use crate::handlers::*;
-use bdk::Error;
 use bdk_macros::{maybe_async, maybe_await};
 use bdk_wallet::bitcoin;
 use clap::Parser;
index 906ea5eef4dee447528808939eb24afbfae4a70f..02fd6edf7448ed5196e052d997a0dfdb782ff559 100644 (file)
@@ -10,7 +10,7 @@
 //!
 //! This module includes all the utility tools used by the App.
 
-use bdk::Error;
+use crate::error::BDKCliError as Error;
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
 
@@ -68,8 +68,7 @@ pub(crate) fn maybe_descriptor_wallet_name(
         wallet_opts.change_descriptor.as_deref(),
         network,
         &Secp256k1::new(),
-    )
-    .map_err(|e| Error::Generic(e.to_string()))?;
+    )?;
     let mut wallet_opts = wallet_opts;
     wallet_opts.wallet = Some(wallet_name);
 
@@ -97,10 +96,10 @@ pub(crate) fn parse_recipient(s: &str) -> Result<(ScriptBuf, u64), String> {
     feature = "rpc"
 ))]
 /// Parse the proxy (Socket:Port) argument from the cli input.
-pub(crate) fn parse_proxy_auth(s: &str) -> Result<(String, String), String> {
+pub(crate) fn parse_proxy_auth(s: &str) -> Result<(String, String), Error> {
     let parts: Vec<_> = s.split(':').collect();
     if parts.len() != 2 {
-        return Err("Invalid format".to_string());
+        return Err(Error::Generic("Invalid format".to_string()));
     }
 
     let user = parts[0].to_string();
@@ -116,9 +115,7 @@ pub fn get_outpoints_for_address(
     client: &Client,
     max_confirmation_height: Option<usize>,
 ) -> Result<Vec<(OutPoint, TxOut)>, Error> {
-    let unspents = client
-        .script_list_unspent(&address.script_pubkey())
-        .map_err(Error::Electrum)?;
+    let unspents = client.script_list_unspent(&address.script_pubkey())?;
 
     unspents
         .iter()
@@ -128,9 +125,7 @@ pub fn get_outpoints_for_address(
         .map(|utxo| {
             let tx = match client.transaction_get(&utxo.tx_hash) {
                 Ok(tx) => tx,
-                Err(e) => {
-                    return Err(e).map_err(Error::Electrum);
-                }
+                Err(e) => return Err(e).map_err(|e| Error::Generic(e.to_string()))?,
             };
 
             Ok((
@@ -145,14 +140,13 @@ pub fn get_outpoints_for_address(
 }
 
 /// Parse a outpoint (Txid:Vout) argument from cli input.
-pub(crate) fn parse_outpoint(s: &str) -> Result<OutPoint, String> {
-    OutPoint::from_str(s).map_err(|e| e.to_string())
+pub(crate) fn parse_outpoint(s: &str) -> Result<OutPoint, Error> {
+    Ok(OutPoint::from_str(s)?)
 }
 
 /// Parse an address string into `Address<NetworkChecked>`.
-pub(crate) fn parse_address(address_str: &str) -> Result<Address, String> {
-    let unchecked_address =
-        Address::from_str(address_str).map_err(|e| format!("Failed to parse address: {}", e))?;
+pub(crate) fn parse_address(address_str: &str) -> Result<Address, Error> {
+    let unchecked_address = Address::from_str(address_str)?;
     Ok(unchecked_address.assume_checked())
 }
 
index 9b44ce92e06d8187fabd89f6461c8a1645f1e562..7bc093433caf886c5186902d9202aee7c3b95a29 100644 (file)
@@ -1,4 +1,5 @@
 use crate::commands::*;
+use crate::error::BDKCliError as Error;
 use crate::handlers::*;
 use crate::nodes::Nodes;
 use crate::utils::*;
@@ -6,14 +7,11 @@ use bdk_wallet::*;
 
 use bitcoin::*;
 
-use bdk_wallet::blockchain::AnyBlockchain;
-use bdk_wallet::database::AnyDatabase;
 use bdk_wallet::miniscript::{MiniscriptKey, Translator};
 use clap::Parser;
 use js_sys::Promise;
 use regex::Regex;
 use std::collections::HashMap;
-use std::error::Error;
 use std::ops::Deref;
 use std::path::PathBuf;
 use std::rc::Rc;
@@ -30,7 +28,7 @@ use serde::Deserialize;
 
 #[wasm_bindgen]
 pub struct WasmWallet {
-    wallet: Rc<Wallet<AnyDatabase>>,
+    wallet: Rc<Wallet>,
     wallet_opts: Rc<WalletOpts>,
     blockchain: Rc<AnyBlockchain>,
     network: Network,
@@ -44,11 +42,8 @@ pub fn log_init() {
 #[wasm_bindgen]
 impl WasmWallet {
     #[wasm_bindgen(constructor)]
-    pub fn new(network: String, wallet_opts: Vec<JsValue>) -> Result<WasmWallet, String> {
-        fn new_inner(
-            network: String,
-            wallet_opts: Vec<JsValue>,
-        ) -> Result<WasmWallet, Box<dyn Error>> {
+    pub fn new(network: String, wallet_opts: Vec<JsValue>) -> Result<WasmWallet, Error> {
+        fn new_inner(network: String, wallet_opts: Vec<JsValue>) -> Result<WasmWallet, Error> {
             // Both open_database and new_blockchain need a home path to be passed
             // in, even tho it won't be used
             let dummy_home_dir = PathBuf::new();
@@ -80,11 +75,11 @@ impl WasmWallet {
 
         async fn run_command_inner(
             command: String,
-            wallet: Rc<Wallet<AnyDatabase>>,
+            wallet: Rc<Wallet>,
             wallet_opts: Rc<WalletOpts>,
             blockchain: Rc<AnyBlockchain>,
             network: Network,
-        ) -> Result<serde_json::Value, Box<dyn Error>> {
+        ) -> Result<serde_json::Value, Error> {
             let split_regex = Regex::new(crate::REPL_LINE_SPLIT_REGEX)?;
             let split_line: Vec<&str> = split_regex
                 .captures_iter(&command)
@@ -131,40 +126,40 @@ struct AliasMap {
 }
 
 #[cfg(feature = "compiler")]
-impl Translator<String, String, bdk::Error> for AliasMap {
+impl Translator<String, String, Error> for AliasMap {
     // Provides the translation public keys P -> Q
-    fn pk(&mut self, pk: &String) -> Result<String, bdk::Error> {
+    fn pk(&mut self, pk: &String) -> Result<String, Error> {
         self.inner
             .get(pk)
             .map(|a| a.into_key())
-            .ok_or(bdk::Error::Generic("Couldn't map alias".to_string())) // Dummy Err
+            .ok_or(Error::Generic("Couldn't map alias".to_string())) // Dummy Err
     }
 
-    fn sha256(&mut self, sha256: &String) -> Result<String, bdk::Error> {
+    fn sha256(&mut self, sha256: &String) -> Result<String, Error> {
         Ok(sha256.to_string())
     }
 
-    fn hash256(&mut self, hash256: &String) -> Result<String, bdk::Error> {
+    fn hash256(&mut self, hash256: &String) -> Result<String, Error> {
         Ok(hash256.to_string())
     }
 
-    fn ripemd160(&mut self, ripemd160: &String) -> Result<String, bdk::Error> {
+    fn ripemd160(&mut self, ripemd160: &String) -> Result<String, Error> {
         Ok(ripemd160.to_string())
     }
 
-    fn hash160(&mut self, hash160: &String) -> Result<String, bdk::Error> {
+    fn hash160(&mut self, hash160: &String) -> Result<String, Error> {
         Ok(hash160.to_string())
     }
 }
 
 #[wasm_bindgen]
 #[cfg(feature = "compiler")]
-pub fn compile(policy: String, aliases: String, script_type: String) -> Result<JsValue, String> {
+pub fn compile(policy: String, aliases: String, script_type: String) -> Result<JsValue, Error> {
     fn compile_inner(
         policy: String,
         aliases: String,
         script_type: String,
-    ) -> Result<String, Box<dyn Error>> {
+    ) -> Result<String, Error> {
         use std::collections::HashMap;
         let aliases: HashMap<String, Alias> = serde_json::from_str(&aliases)?;
         let mut aliases = AliasMap { inner: aliases };
@@ -175,11 +170,10 @@ pub fn compile(policy: String, aliases: String, script_type: String) -> Result<J
             "sh" => Descriptor::new_sh(policy.compile()?)?,
             "wsh" => Descriptor::new_wsh(policy.compile()?)?,
             "sh-wsh" => Descriptor::new_sh_wsh(policy.compile()?)?,
-            _ => return Err(Box::<dyn Error>::from("InvalidScriptType")),
+            _ => return Err(Error::Generic("InvalidScriptType".to_string())),
         };
 
-        let descriptor: Result<Descriptor<String>, bdk::Error> =
-            descriptor.translate_pk(&mut aliases);
+        let descriptor: Result<Descriptor<String>, Error> = descriptor.translate_pk(&mut aliases);
         let descriptor = descriptor?;
 
         Ok(descriptor.to_string().into())
@@ -213,10 +207,8 @@ impl Alias {
                 key.to_wif()
             }
             Alias::GenExt { extra: path } => {
-                let generated: GeneratedKey<
-                    bitcoin::util::bip32::ExtendedPrivKey,
-                    miniscript::Legacy,
-                > = GeneratableDefaultOptions::generate_default().unwrap();
+                let generated: GeneratedKey<bitcoin::bip32::Xpriv, miniscript::Legacy> =
+                    GeneratableDefaultOptions::generate_default().unwrap();
 
                 let mut xprv = generated.into_key();
                 xprv.network = Network::Testnet;