diff --git a/crates/chain/src/indexer/keychain_txout.rs b/crates/chain/src/indexer/keychain_txout.rs index 99931cf5e..4500c0913 100644 --- a/crates/chain/src/indexer/keychain_txout.rs +++ b/crates/chain/src/indexer/keychain_txout.rs @@ -973,25 +973,27 @@ pub enum InsertDescriptorError { }, } -impl core::fmt::Display for InsertDescriptorError { +impl core::fmt::Display for InsertDescriptorError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { InsertDescriptorError::DescriptorAlreadyAssigned { - existing_assignment: existing, + existing_assignment, descriptor, } => { write!( f, - "attempt to re-assign descriptor {descriptor:?} already assigned to {existing:?}" + "descriptor '{}' is already in use by another keychain '{}'", + descriptor, existing_assignment ) } InsertDescriptorError::KeychainAlreadyAssigned { - existing_assignment: existing, + existing_assignment, keychain, } => { write!( f, - "attempt to re-assign keychain {keychain:?} already assigned to {existing:?}" + "keychain '{}' is already associated with another descriptor '{}'", + keychain, existing_assignment ) } } @@ -999,7 +1001,7 @@ impl core::fmt::Display for InsertDescriptorError { } #[cfg(feature = "std")] -impl std::error::Error for InsertDescriptorError {} +impl std::error::Error for InsertDescriptorError {} /// `ChangeSet` represents persistent updates to a [`KeychainTxOutIndex`]. /// diff --git a/crates/chain/src/tx_graph.rs b/crates/chain/src/tx_graph.rs index 97d4ecc02..701de335d 100644 --- a/crates/chain/src/tx_graph.rs +++ b/crates/chain/src/tx_graph.rs @@ -256,15 +256,33 @@ pub enum CalculateFeeError { impl fmt::Display for CalculateFeeError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - CalculateFeeError::MissingTxOut(outpoints) => write!( - f, - "missing `TxOut` for one or more of the inputs of the tx: {outpoints:?}", - ), - CalculateFeeError::NegativeFee(fee) => write!( - f, - "transaction is invalid according to the graph and has negative fee: {}", - fee.display_dynamic() - ), + CalculateFeeError::MissingTxOut(outpoints) => { + let max_show = 3; + let shown: Vec<_> = outpoints.iter().take(max_show).collect(); + let remaining = outpoints.len().saturating_sub(max_show); + + write!(f, "cannot calculate fee, missing previous output(s): ")?; + if outpoints.is_empty() { + write!(f, "") + } else { + write!(f, "{}", shown[0])?; + for op in &shown[1..] { + write!(f, ", {}", op)?; + } + if remaining > 0 { + write!(f, " (+{} more)", remaining)?; + } + Ok(()) + } + } + CalculateFeeError::NegativeFee(fee) => { + write!( + f, + "invalid transaction: negative fee {}", + fee.display_dynamic() + )?; + Ok(()) + } } } } diff --git a/crates/chain/tests/test_keychain_txout_index.rs b/crates/chain/tests/test_keychain_txout_index.rs index 0cce8476d..263a0fa86 100644 --- a/crates/chain/tests/test_keychain_txout_index.rs +++ b/crates/chain/tests/test_keychain_txout_index.rs @@ -18,6 +18,15 @@ enum TestKeychain { Internal, } +impl core::fmt::Display for TestKeychain { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + TestKeychain::External => write!(f, "External"), + TestKeychain::Internal => write!(f, "Internal"), + } + } +} + fn parse_descriptor(descriptor: &str) -> Descriptor { let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only(); Descriptor::::parse_descriptor(&secp, descriptor) diff --git a/crates/core/src/spk_client.rs b/crates/core/src/spk_client.rs index 2c80e70af..8612959cb 100644 --- a/crates/core/src/spk_client.rs +++ b/crates/core/src/spk_client.rs @@ -225,6 +225,7 @@ impl SyncRequestBuilder { /// [`chain_tip`](SyncRequestBuilder::chain_tip) (if provided). /// /// ```rust +/// # use std::io::{self, Write}; /// # use bdk_chain::{bitcoin::{hashes::Hash, ScriptBuf}, local_chain::LocalChain}; /// # use bdk_chain::spk_client::SyncRequest; /// # let (local_chain, _) = LocalChain::from_genesis(Hash::all_zeros()); @@ -236,7 +237,22 @@ impl SyncRequestBuilder { /// // Provide list of scripts to scan for transactions against. /// .spks(scripts) /// // This is called for every synced item. -/// .inspect(|item, progress| println!("{} (remaining: {})", item, progress.remaining())) +/// .inspect(|item, progress| { +/// let pc = (100.0 * progress.consumed() as f32) / progress.total() as f32; +/// match item { +/// // In this example I = (), so the first field of Spk is unit. +/// bdk_chain::spk_client::SyncItem::Spk((), spk) => { +/// eprintln!("[ SCANNING {pc:03.0}% ] script {}", spk); +/// } +/// bdk_chain::spk_client::SyncItem::Txid(txid) => { +/// eprintln!("[ SCANNING {pc:03.0}% ] txid {}", txid); +/// } +/// bdk_chain::spk_client::SyncItem::OutPoint(op) => { +/// eprintln!("[ SCANNING {pc:03.0}% ] outpoint {}", op); +/// } +/// } +/// let _ = io::stderr().flush(); +/// }) /// // Finish constructing the sync request. /// .build(); /// ``` diff --git a/crates/file_store/src/lib.rs b/crates/file_store/src/lib.rs index d7dd35bf4..5a7d50304 100644 --- a/crates/file_store/src/lib.rs +++ b/crates/file_store/src/lib.rs @@ -25,13 +25,24 @@ pub enum StoreError { impl core::fmt::Display for StoreError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + fn fmt_hex_bytes(f: &mut core::fmt::Formatter<'_>, bytes: &[u8]) -> core::fmt::Result { + for &b in bytes { + write!(f, "{:02x}", b)?; + } + Ok(()) + } + match self { - Self::Io(e) => write!(f, "io error trying to read file: {e}"), - Self::InvalidMagicBytes { got, expected } => write!( - f, - "file has invalid magic bytes: expected={expected:?} got={got:?}", - ), - Self::Bincode(e) => write!(f, "bincode error while reading entry {e}"), + Self::Io(e) => write!(f, "io error while reading store file: {}", e), + Self::Bincode(e) => write!(f, "bincode error while decoding entry {}", e), + Self::InvalidMagicBytes { got, expected } => { + write!(f, "invalid magic bytes: ")?; + write!(f, "expected 0x")?; + fmt_hex_bytes(f, expected)?; + write!(f, ", got 0x")?; + fmt_hex_bytes(f, got)?; + Ok(()) + } } } } diff --git a/examples/example_electrum/src/main.rs b/examples/example_electrum/src/main.rs index aa89f07e1..c92666303 100644 --- a/examples/example_electrum/src/main.rs +++ b/examples/example_electrum/src/main.rs @@ -210,9 +210,22 @@ fn main() -> anyhow::Result<()> { .chain_tip(chain_tip.clone()) .inspect(|item, progress| { let pc = (100 * progress.consumed()) as f32 / progress.total() as f32; - eprintln!("[ SCANNING {pc:03.0}% ] {item}"); + match item { + bdk_chain::spk_client::SyncItem::Spk((keychain, index), spk) => { + eprintln!( + "[ SCANNING {pc:3.0}% ] script {} {} {}", + keychain, index, spk + ); + } + bdk_chain::spk_client::SyncItem::Txid(txid) => { + eprintln!("[ SCANNING {pc:3.0}% ] txid {}", txid); + } + bdk_chain::spk_client::SyncItem::OutPoint(op) => { + eprintln!("[ SCANNING {pc:3.0}% ] outpoint {}", op); + } + } + let _ = io::stderr().flush(); }); - let canonical_view = graph.canonical_view( &*chain, chain_tip.block_id(), diff --git a/examples/example_esplora/src/main.rs b/examples/example_esplora/src/main.rs index 99f72391c..9bc3231e6 100644 --- a/examples/example_esplora/src/main.rs +++ b/examples/example_esplora/src/main.rs @@ -215,8 +215,20 @@ fn main() -> anyhow::Result<()> { .chain_tip(local_tip.clone()) .inspect(|item, progress| { let pc = (100 * progress.consumed()) as f32 / progress.total() as f32; - eprintln!("[ SCANNING {pc:03.0}% ] {item}"); - // Flush early to ensure we print at every iteration. + match item { + bdk_chain::spk_client::SyncItem::Spk((keychain, index), spk) => { + eprintln!( + "[ SCANNING {pc:3.0}% ] script {} {} {}", + keychain, index, spk + ); + } + bdk_chain::spk_client::SyncItem::Txid(txid) => { + eprintln!("[ SCANNING {pc:3.0}% ] txid {}", txid); + } + bdk_chain::spk_client::SyncItem::OutPoint(op) => { + eprintln!("[ SCANNING {pc:3.0}% ] outpoint {}", op); + } + } let _ = io::stderr().flush(); });