Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions crates/chain/src/indexer/keychain_txout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
spk_txout::SpkTxOutIndex,
DescriptorExt, DescriptorId, Indexed, Indexer, KeychainIndexed, SpkIterator,
};
use alloc::{borrow::ToOwned, vec::Vec};
use alloc::{borrow::ToOwned, string::String, string::ToString, vec::Vec};
use bitcoin::{
key::Secp256k1, Amount, OutPoint, Script, ScriptBuf, SignedAmount, Transaction, TxOut, Txid,
};
Expand Down Expand Up @@ -973,33 +973,47 @@ pub enum InsertDescriptorError<K> {
},
}

impl<K: core::fmt::Debug> core::fmt::Display for InsertDescriptorError<K> {
/// Returns a shortened string representation of a descriptor.
/// Shows the first and last `edge_len` characters, separated by `...`.
fn short_descriptor(desc: &Descriptor<DescriptorPublicKey>, edge_len: usize) -> String {
let s = ToString::to_string(desc);
if s.len() <= edge_len * 2 {
return s;
}
format!("{}...{}", &s[..edge_len], &s[s.len() - edge_len..])
}

impl<K: core::fmt::Display> core::fmt::Display for InsertDescriptorError<K> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
InsertDescriptorError::DescriptorAlreadyAssigned {
existing_assignment: existing,
existing_assignment,
descriptor,
} => {
let short_desc = short_descriptor(descriptor, 4);
write!(
f,
"attempt to re-assign descriptor {descriptor:?} already assigned to {existing:?}"
"Descriptor '{}' is already in use by keychain '{}'. Please use a different descriptor.",
short_desc, existing_assignment
)
}
InsertDescriptorError::KeychainAlreadyAssigned {
existing_assignment: existing,
existing_assignment,
keychain,
} => {
let short_desc = short_descriptor(existing_assignment, 4);
write!(
f,
"attempt to re-assign keychain {keychain:?} already assigned to {existing:?}"
"Keychain '{}' is already associated with descriptor '{}'. Please choose a different keychain.",
keychain, short_desc
)
}
}
}
}

#[cfg(feature = "std")]
impl<K: core::fmt::Debug> std::error::Error for InsertDescriptorError<K> {}
impl<K: core::fmt::Display + core::fmt::Debug> std::error::Error for InsertDescriptorError<K> {}

/// `ChangeSet` represents persistent updates to a [`KeychainTxOutIndex`].
///
Expand Down
26 changes: 22 additions & 4 deletions crates/chain/src/tx_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,28 @@ 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::MissingTxOut(outpoints) => {
let max_show = 3;
let shown = outpoints.iter().take(max_show);
let remaining = outpoints.len().saturating_sub(max_show);

write!(f, "missing `TxOut` for input(s)")?;
if outpoints.is_empty() {
write!(f, ": <none>")
} else {
write!(f, ": ")?;
for (i, op) in shown.enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", op)?;
}
if remaining > 0 {
write!(f, " (+{} more)", remaining)?;
}
Ok(())
}
}
CalculateFeeError::NegativeFee(fee) => write!(
f,
"transaction is invalid according to the graph and has negative fee: {}",
Expand Down
9 changes: 9 additions & 0 deletions crates/chain/tests/test_keychain_txout_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<DescriptorPublicKey> {
let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, descriptor)
Expand Down
18 changes: 17 additions & 1 deletion crates/core/src/spk_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ impl<I, D> SyncRequestBuilder<I, D> {
/// [`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());
Expand All @@ -236,7 +237,22 @@ impl<I, D> SyncRequestBuilder<I, D> {
/// // 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();
/// ```
Expand Down
17 changes: 12 additions & 5 deletions crates/file_store/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,18 @@ pub enum StoreError {
impl core::fmt::Display for StoreError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
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::Io(e) => write!(f, "io error trying to read file: {}", e),
Self::InvalidMagicBytes { got, expected } => {
write!(f, "file has invalid magic bytes: expected=0x")?;
for b in expected {
write!(f, "{:02x}", b)?;
}
write!(f, " got=0x")?;
for b in got {
write!(f, "{:02x}", b)?;
}
Ok(())
}
Self::Bincode(e) => write!(f, "bincode error while reading entry {e}"),
}
}
Expand Down
17 changes: 15 additions & 2 deletions examples/example_electrum/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:03.0}% ] script {} {} {}",
keychain, index, 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();
});

let canonical_view = graph.canonical_view(
&*chain,
chain_tip.block_id(),
Expand Down
16 changes: 14 additions & 2 deletions examples/example_esplora/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:03.0}% ] script {} {} {}",
keychain, index, 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();
});

Expand Down
Loading