From: Vihiga Tyonum Date: Thu, 21 May 2026 15:20:00 +0000 (+0100) Subject: ref(main): add run to handle routing X-Git-Url: http://internal-gitweb-vhost/blockdata/script/encode/-script/display/struct.HashEngine.html?a=commitdiff_plain;h=f77ddccb9b01ce0fa77e576ca7513554c8e98772;p=bdk-cli ref(main): add run to handle routing - add run fn in main to handle routing --- diff --git a/src/client.rs b/src/client.rs index 6c02441..db31bf6 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,10 +1,7 @@ -use crate::error::BDKCliError as Error; +#[cfg(feature = "rpc")] +use bdk_bitcoind_rpc::{Emitter, bitcoincore_rpc::RpcApi}; #[cfg(feature = "esplora")] use bdk_esplora::EsploraAsyncExt; -use bdk_wallet::{ - bitcoin::{Transaction, Txid}, - chain::CanonicalizationParams, -}; #[cfg(any( feature = "electrum", feature = "esplora", @@ -12,14 +9,16 @@ use bdk_wallet::{ feature = "cbf" ))] use { - crate::commands::{ClientType, WalletOpts}, - - bdk_wallet::Wallet, + crate::commands::WalletOpts, + crate::error::BDKCliError as Error, + bdk_wallet::{ + Wallet, + bitcoin::{Transaction, Txid}, + chain::CanonicalizationParams, + }, + clap::ValueEnum, std::path::PathBuf, }; -#[cfg(feature = "rpc")] -use bdk_bitcoind_rpc::{Emitter, bitcoincore_rpc::RpcApi}; - #[cfg(feature = "cbf")] use { @@ -27,6 +26,24 @@ use { bdk_kyoto::{BuilderExt, LightClient}, }; +#[cfg(any( + feature = "electrum", + feature = "esplora", + feature = "rpc", + feature = "cbf" +))] +#[derive(Clone, ValueEnum, Debug, Eq, PartialEq)] +pub enum ClientType { + #[cfg(feature = "electrum")] + Electrum, + #[cfg(feature = "esplora")] + Esplora, + #[cfg(feature = "rpc")] + Rpc, + #[cfg(feature = "cbf")] + Cbf, +} + #[cfg(any( feature = "electrum", feature = "esplora", @@ -50,9 +67,7 @@ pub(crate) enum BlockchainClient { }, #[cfg(feature = "cbf")] - KyotoClient { - client: Box, - }, + KyotoClient { client: Box }, } #[cfg(any( @@ -83,14 +98,23 @@ impl BlockchainClient { #[cfg(feature = "cbf")] Self::KyotoClient { client } => { - // ... (Kyoto broadcast logic from your online.rs) ... - Ok(tx.compute_txid()) + let txid = tx.compute_txid(); + let wtxid = client + .requester + .broadcast_random(tx.clone()) + .await + .map_err(|_| { + tracing::warn!("Broadcast was unsuccessful"); + Error::Generic("Transaction broadcast timed out after 30 seconds".into()) + })?; + tracing::info!("Successfully broadcast WTXID: {wtxid}"); + Ok(txid) } } } pub async fn sync_wallet(&self, wallet: &mut Wallet) -> Result<(), Error> { - // #[cfg(any(feature = "electrum", feature = "esplora"))] + #[cfg(any(feature = "electrum", feature = "esplora"))] let request = wallet .start_sync_with_revealed_spks() .inspect(|item, progress| { @@ -98,7 +122,7 @@ impl BlockchainClient { eprintln!("[ SCANNING {pc:03.0}% ] {item}"); }); match self { - // #[cfg(feature = "electrum")] + #[cfg(feature = "electrum")] Self::Electrum { client, batch_size } => { // Populate the electrum client's transaction cache so it doesn't re-download transaction we // already have. @@ -109,7 +133,7 @@ impl BlockchainClient { .apply_update(update) .map_err(|e| Error::Generic(e.to_string())) } - // #[cfg(feature = "esplora")] + #[cfg(feature = "esplora")] Self::Esplora { client, parallel_requests, @@ -122,7 +146,7 @@ impl BlockchainClient { .apply_update(update) .map_err(|e| Error::Generic(e.to_string())) } - // #[cfg(feature = "rpc")] + #[cfg(feature = "rpc")] Self::RpcClient { client } => { let blockchain_info = client.get_blockchain_info()?; let wallet_cp = wallet.latest_checkpoint(); @@ -166,7 +190,7 @@ impl BlockchainClient { wallet.apply_unconfirmed_txs(mempool_txs.update); Ok(()) } - // #[cfg(feature = "cbf")] + #[cfg(feature = "cbf")] Self::KyotoClient { client } => sync_kyoto_client(wallet, client) .await .map_err(|e| Error::Generic(e.to_string())), diff --git a/src/config.rs b/src/config.rs index 197dc07..ec6adfa 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,11 +4,11 @@ feature = "rpc", feature = "cbf" ))] -use crate::commands::ClientType; -#[cfg(feature = "sqlite")] -use crate::commands::DatabaseType; +use crate::client::ClientType; use crate::commands::WalletOpts; use crate::error::BDKCliError as Error; +#[cfg(feature = "sqlite")] +use crate::persister::DatabaseType; use bdk_wallet::bitcoin::Network; #[cfg(any(feature = "sqlite", feature = "redb"))] use clap::ValueEnum; diff --git a/src/main.rs b/src/main.rs index 64600be..df5cfc4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ #![doc(html_logo_url = "https://github.com/bitcoindevkit/bdk/raw/master/static/bdk.png")] #![warn(missing_docs)] -mod backend; +mod client; mod commands; mod config; mod error; @@ -22,14 +22,29 @@ mod handlers; feature = "rpc" ))] mod payjoin; +mod persister; mod utils; -mod wallet; +#[cfg(feature = "redb")] +use bdk_redb::Store as RedbStore; use bdk_wallet::bitcoin::Network; -use log::{debug, error, warn}; +use log::{debug, warn}; -use crate::commands::CliOpts; -use crate::handlers::handle_command; +#[cfg(any( + feature = "electrum", + feature = "esplora", + feature = "rpc", + feature = "cbf" +))] +use crate::client::new_blockchain_client; +use crate::commands::{CliOpts, CliSubCommand, WalletSubCommand}; +use crate::error::BDKCliError as Error; +use crate::handlers::{AppCommand, AppContext}; +#[cfg(any(feature = "sqlite", feature = "redb"))] +use crate::persister::{Persister, new_persisted_wallet}; +use crate::utils::output::FormatOutput; +use crate::utils::prepare_wallet_db_dir; +use crate::utils::{load_wallet_config, prepare_home_dir}; use clap::Parser; #[tokio::main] @@ -45,11 +60,123 @@ async fn main() { ) } - match handle_command(cli_opts).await { - Ok(result) => println!("{result}"), - Err(e) => { - error!("{e}"); - std::process::exit(1); - } + if let Err(e) = run(cli_opts).await { + eprintln!("Error: {}", e); + std::process::exit(1); } } + +async fn run(cli_opts: CliOpts) -> Result<(), Error> { + let datadir = cli_opts.datadir.clone(); + let home_dir = prepare_home_dir(datadir)?; + + match cli_opts.subcommand.clone() { + CliSubCommand::Wallet { + wallet: wallet_name, + subcommand, + } => match subcommand { + #[cfg(any( + feature = "electrum", + feature = "esplora", + feature = "rpc", + feature = "cbf" + ))] + WalletSubCommand::OnlineWalletSubCommand(online_cmd) => { + let (wallet_opts, network) = load_wallet_config(&home_dir, &wallet_name)?; + + let database_path = prepare_wallet_db_dir(&home_dir, &wallet_name)?; + #[cfg(any(feature = "sqlite", feature = "redb"))] + let mut persister: Persister = match &wallet_opts.database_type { + #[cfg(feature = "sqlite")] + crate::persister::DatabaseType::Sqlite => { + let db_file = database_path.join("wallet.sqlite"); + let connection = bdk_wallet::rusqlite::Connection::open(db_file)?; + Persister::Connection(connection) + } + #[cfg(feature = "redb")] + crate::persister::DatabaseType::Redb => { + use crate::persister::Persister; + + let db = std::sync::Arc::new(bdk_redb::redb::Database::create( + home_dir.join("wallet.redb"), + )?); + let store = RedbStore::new(db, wallet_name)?; + log::debug!("Redb database opened successfully"); + Persister::RedbStore(store) + } + }; + + let mut wallet = new_persisted_wallet(network, &mut persister, &wallet_opts)?; + + let client = new_blockchain_client(&wallet_opts, &wallet, database_path)?; + + let mut ctx = AppContext::new(network, home_dir) + .with_wallet(&mut wallet) + .with_client(&client); + + online_cmd.execute(&mut ctx).await?; + } + WalletSubCommand::OfflineWalletSubCommand(offline_cmd) => { + let (wallet_opts, network) = load_wallet_config(&home_dir, &wallet_name)?; + + let database_path = prepare_wallet_db_dir(&home_dir, &wallet_name)?; + + #[cfg(any(feature = "sqlite", feature = "redb"))] + let mut persister: Persister = match &wallet_opts.database_type { + #[cfg(feature = "sqlite")] + crate::persister::DatabaseType::Sqlite => { + let db_file = database_path.join("wallet.sqlite"); + let connection = bdk_wallet::rusqlite::Connection::open(db_file)?; + Persister::Connection(connection) + } + #[cfg(feature = "redb")] + crate::persister::DatabaseType::Redb => { + use crate::persister::Persister; + let db = std::sync::Arc::new(bdk_redb::redb::Database::create( + home_dir.join("wallet.redb"), + )?); + let store = RedbStore::new(db, wallet_name)?; + Persister::RedbStore(store) + } + }; + + let mut wallet = new_persisted_wallet(network, &mut persister, &wallet_opts)?; + + let mut ctx = AppContext::new(network, home_dir).with_wallet(&mut wallet); + + offline_cmd.execute(&mut ctx)?; + } + WalletSubCommand::Config(mut config_cmd) => { + config_cmd.wallet_opts.wallet = Some(wallet_name); + + let mut ctx = AppContext::new(cli_opts.network, home_dir); + + config_cmd.execute(&mut ctx)?.print()?; + } + }, + + CliSubCommand::Key { subcommand } => { + let mut ctx = AppContext::new(cli_opts.network, home_dir); + subcommand.execute(&mut ctx)?; + } + CliSubCommand::Descriptor(descriptor_command) => { + let mut ctx = AppContext::new(cli_opts.network, home_dir); + descriptor_command.execute(&mut ctx)?.print()?; + } + CliSubCommand::Wallets(cmd) => { + let mut ctx = AppContext::new(cli_opts.network, home_dir); + cmd.execute(&mut ctx)?.print()?; + } + CliSubCommand::Repl { wallet: _ } => todo!(), + CliSubCommand::Completions { shell } => { + shell; + } + #[cfg(feature = "compiler")] + CliSubCommand::Compile(cmd) => { + let mut ctx = AppContext::new(cli_opts.network, home_dir); + cmd.execute(&mut ctx)?.print()?; + } + }; + + Ok(()) +}