From: Vihiga Tyonum Date: Tue, 18 Feb 2025 20:49:36 +0000 (+0100) Subject: refactor: update onlinewalletsubcommands X-Git-Tag: v1.0.0~7^2~12 X-Git-Url: http://internal-gitweb-vhost/?a=commitdiff_plain;h=0be8453c54fc747c3c4e06b665c100bc0ee7bea6;p=bdk-cli refactor: update onlinewalletsubcommands -update all online wallet subcommands [Ticket: X] --- diff --git a/src/commands.rs b/src/commands.rs index 776a916..9212c20 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -603,29 +603,21 @@ mod test { #[cfg(feature = "compiler")] use crate::handlers::handle_compile_subcommand; use crate::handlers::handle_key_subcommand; + use bdk_wallet::Wallet; #[cfg(all(feature = "reserves", feature = "electrum"))] - use crate::handlers::{handle_ext_reserves_subcommand, handle_online_wallet_subcommand}; - #[cfg(all(feature = "reserves", feature = "electrum"))] - use bdk_electrum::electrum_client::Client; - #[cfg(all(feature = "reserves", feature = "electrum"))] - use bdk_reserves::bdk::SyncOptions; + use { + crate::handlers::{handle_ext_reserves_subcommand, handle_online_wallet_subcommand}, + bdk_electrum::electrum_client::Client, + bdk_electrum::BdkElectrumClient, + bdk_wallet::bitcoin::{consensus::Encodable, Psbt}, + }; + use bdk_wallet::bitcoin::bip32::{DerivationPath, Xpriv}; - #[cfg(all(feature = "reserves", feature = "electrum"))] - use bdk_wallet::bitcoin::{consensus::Encodable, Psbt}; use bdk_wallet::bitcoin::{Address, Network, OutPoint}; use bdk_wallet::miniscript::bitcoin::network::Network::Testnet; - #[cfg(all(feature = "reserves", feature = "electrum"))] - use bdk_wallet::{blockchain::ElectrumBlockchain, database::MemoryDatabase, Wallet}; // yet to fix imports use std::str::{self, FromStr}; use super::OfflineWalletSubCommand::{BumpFee, CreateTx, GetNewAddress}; - #[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" - ))] - use super::OnlineWalletSubCommand::{Broadcast, Sync}; use super::WalletSubCommand::OfflineWalletSubCommand; #[cfg(any( feature = "electrum", @@ -1501,9 +1493,8 @@ mod test { /// Encodes a partially signed transaction as base64 and returns the bytes of the resulting string. #[cfg(all(feature = "reserves", feature = "electrum"))] fn encode_psbt(psbt: Psbt) -> Vec { - let mut encoded = Vec::::new(); - psbt.consensus_encode(&mut encoded).unwrap(); - let base64_psbt = base64::encode(&encoded); + let serialized_psbt = psbt.serialize(); + let base64_psbt = base64::encode(serialized_psbt); base64_psbt.as_bytes().to_vec() } @@ -1515,21 +1506,21 @@ mod test { let message = "Those coins belong to Satoshi Nakamoto"; let client = Client::new("ssl://electrum.blockstream.info:60002").unwrap(); - let blockchain = ElectrumBlockchain::from(client); - let wallet = Wallet::new( - &descriptor, - None, - Network::Testnet, - MemoryDatabase::default(), - ) - .unwrap(); + let blockchain = BdkElectrumClient::new(client); + let mut wallet = Wallet::create_single(descriptor) + .network(Network::Testnet) + .create_wallet_no_persist() + .unwrap(); - wallet.sync(&blockchain, SyncOptions::default()).unwrap(); - let balance = wallet.get_balance().unwrap(); + let scan_request = wallet.start_full_scan(); + + let update = blockchain.full_scan(scan_request, 50, 1, true).unwrap(); + wallet.apply_update(update).unwrap(); + + let balance = wallet.balance(); + + let addr = wallet.reveal_next_address(bdk_wallet::KeychainKind::External); - let addr = wallet - .get_address(bdk_wallet::wallet::AddressIndex::New) - .unwrap(); assert_eq!( "tb1qanjjv4cs20dgv32vncrxw702l8g4qtn2m9wn7d", addr.to_string() @@ -1555,8 +1546,8 @@ mod test { } => online_subcommand, _ => panic!("unexpected subcommand"), }; - let result = handle_online_wallet_subcommand(&wallet, &blockchain, wallet_subcmd).unwrap(); - let psbt: PartiallySignedTransaction = + let result = handle_online_wallet_subcommand(wallet, &blockchain, wallet_subcmd).unwrap(); + let psbt: Psbt = serde_json::from_str(&result.as_object().unwrap().get("psbt").unwrap().to_string()) .unwrap(); let psbt = encode_psbt(psbt); @@ -1593,7 +1584,7 @@ mod test { } => online_subcommand, _ => panic!("unexpected subcommand"), }; - let result = handle_online_wallet_subcommand(&wallet, &blockchain, wallet_subcmd).unwrap(); + let result = handle_online_wallet_subcommand(wallet, &blockchain, wallet_subcmd).unwrap(); let spendable = result .as_object() .unwrap() @@ -1601,7 +1592,7 @@ mod test { .unwrap() .as_u64() .unwrap(); - assert_eq!(spendable, balance.get_spendable()); + assert_eq!(spendable, balance.trusted_spendable().to_sat()); } #[cfg(all(feature = "reserves", feature = "electrum"))] diff --git a/src/error.rs b/src/error.rs index 1f6a824..776dc10 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,7 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum BDKCliError { + #[cfg(feature = "regtest-node")] #[error("Anyhow error: {0}")] Anyhow(#[from] electrsd::corepc_node::anyhow::Error), @@ -20,7 +21,7 @@ pub enum BDKCliError { #[error("Create transaction error: {0}")] CreateTx(#[from] bdk_wallet::error::CreateTxError), - + #[cfg(feature = "regtest-node")] #[error("CoreRPC error: {0}")] CoreRPCError(#[from] electrsd::corepc_client::client_sync::Error), diff --git a/src/handlers.rs b/src/handlers.rs index ceeb533..0fec6c6 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -10,86 +10,78 @@ //! //! This module describes all the command handling logic used by bdk-cli. +use clap::Parser; +use serde_json::json; use std::collections::BTreeMap; use std::convert::TryFrom; +use std::str::FromStr; use crate::commands::OfflineWalletSubCommand::*; -#[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" -))] -use crate::commands::OnlineWalletSubCommand::*; use crate::commands::*; use crate::error::BDKCliError as Error; use crate::utils::*; -#[cfg(all(feature = "reserves", feature = "electrum"))] -use bdk_electrum::electrum_client::{Client, ElectrumApi}; -#[cfg(all(feature = "reserves", feature = "electrum"))] -use bdk_reserves::bdk::bitcoin::psbt::PartiallySignedTransaction; -#[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" -))] -use bdk_reserves::bdk::{ - blockchain::{log_progress, Blockchain}, - SyncOptions, -}; + +use bdk_macros::maybe_async; use bdk_wallet::bip39::{Language, Mnemonic}; use bdk_wallet::bitcoin::bip32::{DerivationPath, KeySource}; -use bdk_wallet::bitcoin::consensus::encode::{serialize, 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::bitcoin::consensus::encode::serialize_hex; +use bdk_wallet::bitcoin::{ + script::PushBytesBuf, secp256k1::Secp256k1, Amount, FeeRate, Network, Psbt, Sequence, Txid, +}; use bdk_wallet::descriptor::Segwitv0; -#[cfg(feature = "compiler")] -use bdk_wallet::descriptor::{Descriptor, Legacy, Miniscript}; -use bdk_wallet::keys::bip39::WordCount; -use bdk_wallet::rusqlite::Connection; -use bdk_wallet::{KeychainKind, PersistedWallet, SignOptions}; -use clap::Parser; - -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_wallet::keys::{ + bip39::WordCount, DerivableKey, DescriptorKey, DescriptorKey::Secret, ExtendedKey, + GeneratableKey, GeneratedKey, }; +use bdk_wallet::miniscript::miniscript; +use bdk_wallet::rusqlite::Connection; +use bdk_wallet::{KeychainKind, SignOptions, Wallet}; -use bdk_macros::maybe_async; #[cfg(any( feature = "electrum", feature = "esplora", feature = "compact_filters", feature = "rpc" ))] -use bdk_macros::maybe_await; +use { + crate::commands::OnlineWalletSubCommand::*, + bdk_reserves::bdk::{ + blockchain::{log_progress, Blockchain}, + SyncOptions, + }, + bdk_wallet::Wallet, +}; + #[cfg(all(feature = "reserves", feature = "electrum"))] -use bdk_reserves::bdk::blockchain::Capability; +use { + bdk_electrum::electrum_client::{Client, ElectrumApi}, + bdk_reserves::bdk::{bitcoin::psbt::PartiallySignedTransaction, blockchain::Capability}, + bdk_wallet::bitcoin::Address, +}; + #[cfg(feature = "reserves")] use bdk_reserves::reserves::{verify_proof, ProofOfReserves}; -#[cfg(all(feature = "reserves", feature = "electrum"))] -use bdk_wallet::bitcoin::Address; +#[cfg(feature = "hardware-signer")] +use hwi::{ + types::{HWIChain, HWIDescriptor}, + HWIClient, +}; #[cfg(feature = "compiler")] -use bdk_wallet::miniscript::policy::Concrete; -#[cfg(feature = "repl")] -use regex::Regex; +use { + bdk_wallet::descriptor::{Descriptor, Legacy, Miniscript}, + bdk_wallet::miniscript::policy::Concrete, +}; #[cfg(feature = "repl")] -use rustyline::{error::ReadlineError, Editor}; -use serde_json::json; -use std::str::FromStr; +use { + regex::Regex, + rustyline::{error::ReadlineError, Editor}, +}; /// Execute an offline wallet sub-command /// /// Offline wallet sub-commands are described in [`OfflineWalletSubCommand`]. pub fn handle_offline_wallet_subcommand( - wallet: &mut PersistedWallet, + wallet: &mut Wallet, wallet_opts: &WalletOpts, offline_subcommand: OfflineWalletSubCommand, ) -> Result { @@ -196,12 +188,11 @@ pub fn handle_offline_wallet_subcommand( let psbt = tx_builder.finish()?; - let serialized_psbt = psbt.serialize(); - let psbt_base64 = base64::encode(&serialized_psbt); + let psbt_base64 = psbt.to_string(); if wallet_opts.verbose { Ok( - json!({"psbt": psbt_base64, "serialized_psbt": serialized_psbt, "details": psbt}), + json!({"psbt": psbt_base64, "serialized_psbt": psbt.serialize(), "details": psbt}), ) } else { Ok(json!({"psbt": psbt_base64, "details": psbt})) @@ -241,10 +232,7 @@ pub fn handle_offline_wallet_subcommand( let psbt = tx_builder.finish()?; - let serialized_psbt = psbt.serialize(); - let psbt_base64 = base64::encode(serialized_psbt); - - Ok(json!({"psbt": psbt_base64, "details": psbt})) + Ok(json!({"psbt": psbt.to_string(), "details": psbt})) } Policies => { let external_policy = wallet.policies(KeychainKind::External)?; @@ -274,12 +262,10 @@ pub fn handle_offline_wallet_subcommand( 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}), + json!({"psbt": psbt.to_string(),"is_finalized": finalized, "serialized_psbt": psbt}), ) } else { - Ok( - json!({"psbt": base64::encode(serialize(&psbt_bytes)),"is_finalized": finalized,}), - ) + Ok(json!({"psbt": psbt.to_string(),"is_finalized": finalized,})) } } ExtractPsbt { psbt } => { @@ -304,12 +290,10 @@ pub fn handle_offline_wallet_subcommand( 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}), + json!({ "psbt": psbt.to_string(),"is_finalized": finalized, "serialized_psbt": psbt}), ) } else { - Ok( - json!({ "psbt": base64::encode(serialize(&psbt_bytes)),"is_finalized": finalized,}), - ) + Ok(json!({ "psbt": psbt.to_string(),"is_finalized": finalized,})) } } CombinePsbt { psbt } => { @@ -331,7 +315,7 @@ pub fn handle_offline_wallet_subcommand( Ok(acc) }, )?; - Ok(json!({ "psbt": base64::encode(final_psbt.serialize()) })) + Ok(json!({ "psbt": final_psbt.to_string() })) } } } @@ -347,7 +331,7 @@ pub fn handle_offline_wallet_subcommand( feature = "rpc" ))] pub(crate) fn handle_online_wallet_subcommand( - wallet: &Wallet, + wallet: Wallet, blockchain: &B, online_subcommand: OnlineWalletSubCommand, ) -> Result { @@ -369,7 +353,7 @@ pub(crate) fn handle_online_wallet_subcommand( is_final(&psbt)?; psbt.extract_tx() } - (None, Some(tx)) => Psbt::deserialize(&Vec::::from_hex(&tx)?)?, + (None, Some(tx)) => Psbt::deserialize(&Vec::::from_hex(&tx)?), (Some(_), Some(_)) => panic!("Both `psbt` and `tx` options not allowed"), (None, None) => panic!("Missing `psbt` and `tx` option"), }; @@ -388,8 +372,7 @@ pub(crate) fn handle_online_wallet_subcommand( }, )?; - let psbt_ser = serialize(&psbt); - let psbt_b64 = base64::encode(&psbt_ser); + let psbt_b64 = psbt.to_string(); Ok(json!({ "psbt": psbt , "psbt_base64" : psbt_b64})) } diff --git a/src/utils.rs b/src/utils.rs index f3df441..40cd95a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -9,7 +9,6 @@ //! Utility Tools //! //! This module includes all the utility tools used by the App. - use crate::commands::WalletOpts; use crate::error::BDKCliError as Error; use crate::nodes::Nodes; @@ -32,32 +31,17 @@ use { #[cfg(all(feature = "reserves", feature = "electrum"))] use { bdk_electrum::electrum_client::{Client, ElectrumApi}, + bdk_reserves::bdk::blockchain::ConfigurableBlockchain, bdk_wallet::bitcoin::TxOut, electrsd::corepc_client::client_sync::v17::blockchain, }; -#[cfg(feature = "esplora")] -// use bdk::blockchain::esplora::EsploraBlockchainConfig; //haven't gotten import -use bdk_bitcoind_rpc::bitcoincore_rpc::Auth; -use bdk_esplora::EsploraAsyncExt; -#[cfg(feature = "compact_filters")] -use bdk_reserves::bdk::blockchain::compact_filters::{ - BitcoinPeerConfig, CompactFiltersBlockchainConfig, -}; // can't find compact_filters -use bdk_reserves::bdk::blockchain::ConfigurableBlockchain; -#[cfg(feature = "rpc")] -use bdk_wallet::blockchain::rpc::{RpcConfig, RpcSyncParams}; -#[cfg(feature = "electrum")] -use bdk_wallet::blockchain::ElectrumBlockchainConfig; -#[cfg(any( - feature = "electrum", - feature = "esplora", - feature = "compact_filters", - feature = "rpc" -))] -use bdk_wallet::blockchain::{AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain}; -#[cfg(feature = "sqlite-db")] use bdk_wallet::rusqlite::{Connection, OpenFlags}; +#[cfg(feature = "esplora")] +use { + bdk_bitcoind_rpc::bitcoincore_rpc::{self, Auth}, + bdk_esplora::{esplora_client, EsploraAsyncExt}, +}; /// Create a randomized wallet name from the descriptor checksum. /// If wallet options already includes a name, use that instead. @@ -332,6 +316,20 @@ pub(crate) fn new_backend(_datadir: &Path) -> Result { Ok(backend) } +#[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc",))] +pub(crate) enum BlockchainClient { + #[cfg(feature = "electrum")] + Electrum { + client: bdk_electrum::BdkElectrumClient, + batch_size: usize, + }, + #[cfg(feature = "esplora")] + Esplora { + client: bdk_esplora::esplora_client::AsyncClient, + parallel_requests: usize, + }, +} + #[cfg(any( feature = "electrum", feature = "esplora", @@ -341,50 +339,41 @@ pub(crate) fn new_backend(_datadir: &Path) -> Result { /// Create a new blockchain for a given [Nodes] if available /// or else create one from the wallet configuration options. pub(crate) fn new_blockchain( - _network: Network, wallet_opts: &WalletOpts, _backend: &Nodes, - _home_dir: &Path, -) -> Result { +) -> Result { #[cfg(feature = "electrum")] - let config = { - let url = match _backend { - #[cfg(feature = "regtest-electrum")] - Nodes::Electrum { electrsd, .. } => &electrsd.electrum_url, - _ => &wallet_opts.electrum_opts.server, - }; - - AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig { - url: url.to_owned(), - socks5: wallet_opts.proxy_opts.proxy.clone(), - retry: wallet_opts.proxy_opts.retries, - timeout: wallet_opts.electrum_opts.timeout, - stop_gap: wallet_opts.electrum_opts.stop_gap, - validate_domain: true, - }) - }; + let client = match _backend { + #[cfg(feature = "regtest-electrum")] + Nodes::Electrum { electrsd, .. } => { + let sock5 = Socks5Config::new(wallet_opts.proxy_opts.proxy.unwrap()); + + let electrum_config = ConfigBuilder::new() + .retry(wallet_opts.proxy_opts.retries) + .socks5(Some(sock5)) + .timeout(wallet_opts.electrum_opts.timeout) + .validate_domain(true) + .build(); + + let client = + electrum_client::Client::from_config(&electrsd.electrum_url, electrum_config) + .unwrap(); + } - #[cfg(feature = "esplora")] - let config = { - let url = match _backend { - #[cfg(any(feature = "regtest-esplora-ureq", feature = "regtest-esplora-reqwest"))] - Nodes::Esplora { esplorad, bitcoind } => { - esplorad.esplora_url.expect("Esplora url expected") - } - _ => wallet_opts.esplora_opts.server.clone(), - }; + #[cfg(feature = "esplora")] + Nodes::Esplora { esplorad, bitcoind } => { + let client = esplora_client::Builder::new(&esplorad.electrum_url.as_str()) + .timeout(wallet_opts.esplora_opts.timeout) + .proxy(wallet_opts.proxy_opts.proxy.unwrap().as_str()) + .build_blocking(); - AnyBlockchainConfig::Esplora(EsploraBlockchainConfig { - base_url: url, - timeout: Some(wallet_opts.esplora_opts.timeout), - concurrency: Some(wallet_opts.esplora_opts.conc), - stop_gap: wallet_opts.esplora_opts.stop_gap, - proxy: wallet_opts.proxy_opts.proxy.clone(), - }) + client + } }; #[cfg(feature = "compact_filters")] let config = { + return unimplemented!(); let mut peers = vec![]; for addrs in wallet_opts.compactfilter_opts.address.clone() { for _ in 0..wallet_opts.compactfilter_opts.conn_count { @@ -397,19 +386,19 @@ pub(crate) fn new_blockchain( } let wallet_name = wallet_opts.wallet.as_ref().expect("wallet name"); - AnyBlockchainConfig::CompactFilters(CompactFiltersBlockchainConfig { - peers, - network: _network, - storage_dir: prepare_bc_dir(wallet_name, _home_dir)? - .into_os_string() - .into_string() - .map_err(|_| Error::Generic("Internal OS_String conversion error".to_string()))?, - skip_blocks: Some(wallet_opts.compactfilter_opts.skip_blocks), - }) + // CompactFilters(CompactFiltersBlockchainConfig { + // peers, + // network: _network, + // storage_dir: prepare_bc_dir(wallet_name, _home_dir)? + // .into_os_string() + // .into_string() + // .map_err(|_| Error::Generic("Internal OS_String conversion error".to_string()))?, + // skip_blocks: Some(wallet_opts.compactfilter_opts.skip_blocks), + // }) }; #[cfg(feature = "rpc")] - let config: AnyBlockchainConfig = { + let config = { let (url, auth) = match _backend { #[cfg(feature = "regtest-node")] Nodes::Bitcoin { bitcoind } => ( @@ -435,22 +424,8 @@ pub(crate) fn new_blockchain( let rpc_url = "http://".to_string() + &url; - let rpc_config = RpcConfig { - url: rpc_url, - auth, - network: _network, - wallet_name, - // TODO add cli options to set all rpc sync params - sync_params: Some(RpcSyncParams { - start_time: wallet_opts.rpc_opts.start_time, - ..Default::default() - }), - }; - - AnyBlockchainConfig::Rpc(rpc_config) + bdk_bitcoind_rpc::bitcoincore_rpc::Client::new(&rpc_url, auth).unwrap() }; - - AnyBlockchain::from_config(&config) } /// Create a new wallet from given wallet configuration options.