#[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",
/// 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<u8> {
- let mut encoded = Vec::<u8>::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()
}
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()
} => 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);
} => 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()
.unwrap()
.as_u64()
.unwrap();
- assert_eq!(spendable, balance.get_spendable());
+ assert_eq!(spendable, balance.trusted_spendable().to_sat());
}
#[cfg(all(feature = "reserves", feature = "electrum"))]
#[derive(Debug, Error)]
pub enum BDKCliError {
+ #[cfg(feature = "regtest-node")]
#[error("Anyhow error: {0}")]
Anyhow(#[from] electrsd::corepc_node::anyhow::Error),
#[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),
//!
//! 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<Connection>,
+ wallet: &mut Wallet,
wallet_opts: &WalletOpts,
offline_subcommand: OfflineWalletSubCommand,
) -> Result<serde_json::Value, Error> {
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}))
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)?;
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 } => {
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 } => {
Ok(acc)
},
)?;
- Ok(json!({ "psbt": base64::encode(final_psbt.serialize()) }))
+ Ok(json!({ "psbt": final_psbt.to_string() }))
}
}
}
feature = "rpc"
))]
pub(crate) fn handle_online_wallet_subcommand(
- wallet: &Wallet,
+ wallet: Wallet,
blockchain: &B,
online_subcommand: OnlineWalletSubCommand,
) -> Result<serde_json::Value, Error> {
is_final(&psbt)?;
psbt.extract_tx()
}
- (None, Some(tx)) => Psbt::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"),
};
},
)?;
- 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}))
}
//! 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;
#[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.
Ok(backend)
}
+#[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc",))]
+pub(crate) enum BlockchainClient {
+ #[cfg(feature = "electrum")]
+ Electrum {
+ client: bdk_electrum::BdkElectrumClient<bdk_electrum::electrum_client::Client>,
+ batch_size: usize,
+ },
+ #[cfg(feature = "esplora")]
+ Esplora {
+ client: bdk_esplora::esplora_client::AsyncClient,
+ parallel_requests: usize,
+ },
+}
+
#[cfg(any(
feature = "electrum",
feature = "esplora",
/// 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<AnyBlockchain, Error> {
+) -> Result<BlockchainClient, Error> {
#[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 {
}
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 } => (
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.