[[package]]
name = "bdk_bitcoind_rpc"
-version = "0.21.0"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a52a57cc5e07e207711a5064def0bd04b18e183b9f2247f41561cb12dd393be"
+checksum = "99a9a7a93a0c377d1858263122afe0565e6b38800fe39b2025f217e01f146294"
dependencies = [
"bdk_core",
"bitcoin",
[[package]]
name = "bdk_core"
-version = "0.6.2"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dbbe4aad0c898bfeb5253c222be3ea3dccfb380a07e72c87e3e4ed6664a6753"
+checksum = "cb3028782f6bf14a6df987244333d34e6b272b5a40a53e4879ec2dfd82275a3a"
dependencies = [
"bitcoin",
"hashbrown 0.14.5",
[[package]]
name = "bdk_electrum"
-version = "0.23.2"
+version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b59a3f7fbe678874fa34354097644a171276e02a49934c13b3d61c54610ddf39"
+checksum = "00a9846105bf6e751adbb6946b000ff919ce24a15a941cbfe68485549b552a9c"
dependencies = [
"bdk_core",
"electrum-client",
[[package]]
name = "bdk_esplora"
-version = "0.22.1"
+version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c9f5961444b5f51b9c3937e729a212363d0e4cde6390ded6e01e16292078df4"
+checksum = "83986307ea92997c3d051e8c306af8115a05add601e22acb7c1903008e6b614e"
dependencies = [
"async-trait",
"bdk_core",
[[package]]
name = "bdk_wallet"
-version = "2.1.0"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d30b5dba770184863b5d966ccbc6a11d12c145450be3b6a4435308297e6a12dc"
+checksum = "c99821af39c7df004bd411ece2ef22d9fba5a631b4489f3d9a0ac0d19637e2d0"
dependencies = [
"bdk_chain",
"bip39",
[[package]]
name = "electrum-client"
-version = "0.24.1"
+version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5059f13888a90486e7268bbce59b175f5f76b1c55e5b9c568ceaa42d2b8507c"
+checksum = "1970c5d7bd9de6d4041cbfc3e46faa3e85fe7efcea2b0eb06750d3ae1ef577b7"
dependencies = [
"bitcoin",
"byteorder",
[[package]]
name = "esplora-client"
-version = "0.12.1"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0af349d96a5d9ad77ba59f1437aa6f348b03c5865d4f7d6e7a662d60aedce39"
+checksum = "f19e3ea99dbfbef0c1ec26d83e69de0c579f6aa6aaac4f44597805fcc27e97af"
dependencies = [
"bitcoin",
"hex-conservative 0.2.2",
"minreq",
"reqwest 0.12.28",
"serde",
+ "serde_json",
"tokio",
]
license = "MIT"
[dependencies]
-bdk_wallet = { version = "2.1.0", features = ["rusqlite", "keys-bip39", "compiler", "std"] }
+bdk_wallet = { version = "2.4.0", features = ["rusqlite", "keys-bip39", "compiler", "std"] }
clap = { version = "4.6", features = ["derive","env"] }
clap_complete = "4.6"
dirs = { version = "6.0.0" }
serde= {version = "1.0", features = ["derive"]}
# Optional dependencies
-bdk_bitcoind_rpc = { version = "0.21.0", features = ["std"], optional = true }
-bdk_electrum = { version = "0.23.2", optional = true }
-bdk_esplora = { version = "0.22.1", features = ["async-https", "tokio"], optional = true }
+bdk_bitcoind_rpc = { version = "0.22.0", features = ["std"], optional = true }
+bdk_electrum = { version = "0.24.0", optional = true }
+bdk_esplora = { version = "0.22.2", features = ["async-https", "tokio"], optional = true }
bdk_kyoto = { version = "0.15.4", optional = true }
bdk_redb = { version = "0.1.1", optional = true }
bdk_sp = { version = "0.1.0", optional = true, git = "https://github.com/bitcoindevkit/bdk-sp", tag = "v0.1.0" }
crate::payjoin::{PayjoinManager, ohttp::RelayManager},
crate::utils::is_final,
crate::utils::output::FormatOutput,
+ crate::utils::print_wallet_events,
crate::utils::types::{StatusResult, TransactionResult},
bdk_wallet::bitcoin::{
Psbt, Transaction, Txid, base64::Engine, base64::prelude::BASE64_STANDARD,
Electrum { client, batch_size } => {
client.populate_tx_cache(wallet.tx_graph().full_txs().map(|tx_node| tx_node.tx));
let update = client.full_scan(request, self.stop_gap, *batch_size, false)?;
- wallet.apply_update(update)?;
+ let events = wallet.apply_update_events(update)?;
+ print_wallet_events(&events);
}
#[cfg(feature = "esplora")]
.full_scan(request, self.stop_gap, *parallel_requests)
.await
.map_err(|e| *e)?;
- wallet.apply_update(update)?;
+ let events = wallet.apply_update_events(update)?;
+ print_wallet_events(&events);
}
#[cfg(feature = "rpc")]
RpcClient { client } => {
NO_EXPECTED_MEMPOOL_TXS,
);
- while let Some(block_event) = emitter.next_block()? {
- if block_event.block_height() % 10_000 == 0 {
- let percent_done = f64::from(block_event.block_height())
- / f64::from(blockchain_info.headers as u32)
- * 100f64;
- println!(
- "Applying block at height: {}, {:.2}% done.",
+ let block_events = wallet.events_helper(|w| {
+ while let Some(block_event) = emitter.next_block()? {
+ if block_event.block_height() % 10_000 == 0 {
+ let percent_done = f64::from(block_event.block_height())
+ / f64::from(blockchain_info.headers as u32)
+ * 100f64;
+ println!(
+ "Applying block at height: {}, {:.2}% done.",
+ block_event.block_height(),
+ percent_done
+ );
+ }
+
+ w.apply_block_connected_to(
+ &block_event.block,
block_event.block_height(),
- percent_done
- );
+ block_event.connected_to(),
+ )?;
}
-
- wallet.apply_block_connected_to(
- &block_event.block,
- block_event.block_height(),
- block_event.connected_to(),
- )?;
- }
+ Ok::<_, Error>(())
+ })?;
+ print_wallet_events(&block_events);
let mempool_txs = emitter.mempool()?;
- wallet.apply_unconfirmed_txs(mempool_txs.update);
+ let mempool_events = wallet.apply_unconfirmed_txs_events(mempool_txs.update);
+ print_wallet_events(&mempool_events);
+
+ let evicted_events = wallet.apply_evicted_txs_events(mempool_txs.evicted);
+ print_wallet_events(&evicted_events);
}
#[cfg(feature = "cbf")]
client.populate_tx_cache(wallet.tx_graph().full_txs().map(|tx_node| tx_node.tx));
let update = client.sync(request, *batch_size, false)?;
- wallet
- .apply_update(update)
- .map_err(|e| Error::Generic(e.to_string()))?;
+ let events = wallet.apply_update_events(update)?;
+ print_wallet_events(&events);
}
#[cfg(feature = "esplora")]
Esplora {
.sync(request, *parallel_requests)
.await
.map_err(|e| *e)?;
- wallet
- .apply_update(update)
- .map_err(|e| Error::Generic(e.to_string()))?;
+ let events = wallet.apply_update_events(update)?;
+ print_wallet_events(&events);
}
#[cfg(feature = "rpc")]
RpcClient { client } => {
.filter(|tx| tx.chain_position.is_unconfirmed()),
);
- while let Some(block_event) = emitter.next_block()? {
- if block_event.block_height() % 10_000 == 0 {
- let percent_done = f64::from(block_event.block_height())
- / f64::from(blockchain_info.headers as u32)
- * 100f64;
- println!(
- "Applying block at height: {}, {:.2}% done.",
+ let block_events = wallet.events_helper(|w| {
+ while let Some(block_event) = emitter.next_block()? {
+ if block_event.block_height() % 10_000 == 0 {
+ let percent_done = f64::from(block_event.block_height())
+ / f64::from(blockchain_info.headers as u32)
+ * 100f64;
+ println!(
+ "Applying block at height: {}, {:.2}% done.",
+ block_event.block_height(),
+ percent_done
+ );
+ }
+
+ w.apply_block_connected_to(
+ &block_event.block,
block_event.block_height(),
- percent_done
- );
+ block_event.connected_to(),
+ )?;
}
-
- wallet.apply_block_connected_to(
- &block_event.block,
- block_event.block_height(),
- block_event.connected_to(),
- )?;
- }
+ Ok::<_, Error>(())
+ })?;
+ print_wallet_events(&block_events);
let mempool_txs = emitter.mempool()?;
- wallet.apply_unconfirmed_txs(mempool_txs.update);
+ let mempool_events = wallet.apply_unconfirmed_txs_events(mempool_txs.update);
+ print_wallet_events(&mempool_events);
+
+ let evicted_events = wallet.apply_evicted_txs_events(mempool_txs.evicted);
+ print_wallet_events(&evicted_events);
}
#[cfg(feature = "cbf")]
KyotoClient { client } => sync_kyoto_client(wallet, client)
feature = "cbf",
feature = "rpc"
))]
-use bdk_wallet::bitcoin::Psbt;
+use bdk_wallet::{bitcoin::Psbt, event::WalletEvent};
use bdk_wallet::bitcoin::{Address, Network, OutPoint, ScriptBuf};
use crate::commands::OfflineWalletSubCommand;
OfflineWalletSubCommand::CreateSpTx(_) => false,
}
}
+
+/// Print a human-readable summary of the wallet events produced by a sync or scan.
+///
+/// Emitted to stderr so it does not pollute the JSON result on stdout.
+#[cfg(any(
+ feature = "electrum",
+ feature = "esplora",
+ feature = "rpc",
+ feature = "cbf"
+))]
+pub fn print_wallet_events(events: &[WalletEvent]) {
+ for event in events {
+ match event {
+ WalletEvent::ChainTipChanged { old_tip, new_tip } => {
+ eprintln!(
+ "Chain tip advanced from height {} to {}",
+ old_tip.height, new_tip.height
+ );
+ }
+ WalletEvent::TxConfirmed {
+ txid,
+ block_time,
+ old_block_time,
+ ..
+ } => match old_block_time {
+ Some(old) => eprintln!(
+ "Transaction {txid} re-confirmed at height {} (was height {}, likely a reorg)",
+ block_time.block_id.height, old.block_id.height
+ ),
+ None => eprintln!(
+ "Transaction {txid} confirmed at height {}",
+ block_time.block_id.height
+ ),
+ },
+ WalletEvent::TxUnconfirmed {
+ txid,
+ old_block_time,
+ ..
+ } => match old_block_time {
+ Some(old) => eprintln!(
+ "Transaction {txid} became unconfirmed (was confirmed at height {}, likely a reorg)",
+ old.block_id.height
+ ),
+ None => eprintln!("Transaction {txid} seen in mempool"),
+ },
+ WalletEvent::TxReplaced {
+ txid, conflicts, ..
+ } => {
+ let ids: Vec<String> = conflicts.iter().map(|(_, c)| c.to_string()).collect();
+ eprintln!(
+ "Transaction {txid} was replaced (conflicts with: {})",
+ ids.join(", ")
+ );
+ }
+ WalletEvent::TxDropped { txid, .. } => {
+ eprintln!("Transaction {txid} dropped from the mempool");
+ }
+ _ => {}
+ }
+ }
+}