Skip to content

Commit 95a9633

Browse files
committed
feat: add optional tracing instrumentation
1 parent d9c2b95 commit 95a9633

File tree

7 files changed

+256
-3
lines changed

7 files changed

+256
-3
lines changed

crates/bitcoind_rpc/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ workspace = true
1919
bitcoin = { version = "0.32.0", default-features = false }
2020
bitcoincore-rpc = { version = "0.19.0" }
2121
bdk_core = { path = "../core", version = "0.6.0", default-features = false }
22+
tracing = { version = "0.1", optional = true }
2223

2324
[dev-dependencies]
2425
bdk_bitcoind_rpc = { path = "." }
@@ -29,6 +30,7 @@ bdk_chain = { path = "../chain" }
2930
default = ["std"]
3031
std = ["bitcoin/std", "bdk_core/std"]
3132
serde = ["bitcoin/serde", "bdk_core/serde"]
33+
tracing-logs = ["tracing"]
3234

3335
[[example]]
3436
name = "filter_iter"

crates/bitcoind_rpc/src/lib.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ use bitcoin::{Block, BlockHash, Transaction, Txid};
1414
use bitcoincore_rpc::{bitcoincore_rpc_json, RpcApi};
1515
use std::{collections::HashSet, ops::Deref};
1616

17+
#[cfg(feature = "tracing-logs")]
18+
use tracing::trace;
19+
1720
pub mod bip158;
1821

1922
pub use bitcoincore_rpc;
@@ -115,6 +118,13 @@ where
115118
pub fn mempool(&mut self) -> Result<MempoolEvent, bitcoincore_rpc::Error> {
116119
let client = &*self.client;
117120

121+
#[cfg(feature = "tracing-logs")]
122+
trace!(
123+
"mempool(): entering, start_height={}, last_mempool_tip={:?}",
124+
self.start_height,
125+
self.last_mempool_tip
126+
);
127+
118128
// This is the emitted tip height during the last mempool emission.
119129
let prev_mempool_tip = self
120130
.last_mempool_tip
@@ -125,6 +135,12 @@ where
125135
// Loop to make sure that the fetched mempool content and the fetched tip are consistent
126136
// with one another.
127137
let (raw_mempool, raw_mempool_txids, rpc_height, rpc_block_hash) = loop {
138+
#[cfg(feature = "tracing-logs")]
139+
trace!(
140+
"mempool(): checking consistency loop, prev_mempool_tip={}",
141+
prev_mempool_tip
142+
);
143+
128144
// Determine if height and hash matches the best block from the RPC. Evictions are
129145
// deferred if we are not at the best block.
130146
let height = client.get_block_count()?;
@@ -140,9 +156,25 @@ where
140156
}
141157
};
142158

159+
#[cfg(feature = "tracing-logs")]
160+
trace!(
161+
"mempool(): fetched raw_mempool ({} entries), rpc_height={}, rpc_block_hash={}",
162+
raw_mempool.len(),
163+
rpc_height,
164+
rpc_block_hash
165+
);
166+
143167
let at_tip =
144168
rpc_height == self.last_cp.height() as u64 && rpc_block_hash == self.last_cp.hash();
145169

170+
#[cfg(feature = "tracing-logs")]
171+
trace!(
172+
"mempool(): at_tip={} (last_cp.height={} vs rpc_height={})",
173+
at_tip,
174+
self.last_cp.height(),
175+
rpc_height
176+
);
177+
146178
// If at tip, any expected txid missing from raw mempool is considered evicted;
147179
// if not at tip, we don't evict anything.
148180
let evicted_txids: HashSet<Txid> = if at_tip {
@@ -166,6 +198,14 @@ where
166198
.filter_map({
167199
let latest_time = &mut latest_time;
168200
move |(txid, tx_entry)| -> Option<Result<_, bitcoincore_rpc::Error>> {
201+
#[cfg(feature = "tracing-logs")]
202+
trace!(
203+
"mempool(): considering txid={}, time={}, prev_time={}",
204+
txid,
205+
tx_entry.time,
206+
prev_mempool_time
207+
);
208+
169209
let tx_time = tx_entry.time as usize;
170210
if tx_time > *latest_time {
171211
*latest_time = tx_time;
@@ -206,6 +246,14 @@ where
206246
.extend(new_txs.iter().map(|(tx, _)| tx.compute_txid()));
207247
}
208248

249+
#[cfg(feature = "tracing-logs")]
250+
trace!(
251+
"mempool(): emitting {} new_txs, {} evicted_txids, latest_update_time={}",
252+
new_txs.len(),
253+
evicted_txids.len(),
254+
latest_time
255+
);
256+
209257
Ok(MempoolEvent {
210258
new_txs,
211259
evicted_txids,
@@ -215,11 +263,23 @@ where
215263

216264
/// Emit the next block height and block (if any).
217265
pub fn next_block(&mut self) -> Result<Option<BlockEvent<Block>>, bitcoincore_rpc::Error> {
266+
#[cfg(feature = "tracing-logs")]
267+
trace!(
268+
"next_block(): entering, last_block={:?}",
269+
self.last_block.as_ref().map(|r| r.height)
270+
);
271+
218272
if let Some((checkpoint, block)) = poll(self, move |hash, client| client.get_block(hash))? {
219273
// Stop tracking unconfirmed transactions that have been confirmed in this block.
220274
for tx in &block.txdata {
221275
self.expected_mempool_txids.remove(&tx.compute_txid());
222276
}
277+
#[cfg(feature = "tracing-logs")]
278+
trace!(
279+
"next_block(): emitting block at height={}, txcount={}",
280+
checkpoint.height(),
281+
block.txdata.len()
282+
);
223283
return Ok(Some(BlockEvent { block, checkpoint }));
224284
}
225285
Ok(None)
@@ -313,6 +373,13 @@ where
313373
C: Deref,
314374
C::Target: RpcApi,
315375
{
376+
#[cfg(feature = "tracing-logs")]
377+
trace!(
378+
"poll_once(): last_block={:?}, start_height={}",
379+
emitter.last_block.as_ref().map(|r| r.height),
380+
emitter.start_height
381+
);
382+
316383
let client = &*emitter.client;
317384

318385
if let Some(last_res) = &emitter.last_block {
@@ -321,21 +388,36 @@ where
321388
let next_hash = client.get_block_hash(emitter.start_height as _)?;
322389
// make sure last emission is still in best chain
323390
if client.get_block_hash(last_res.height as _)? != last_res.hash {
391+
#[cfg(feature = "tracing-logs")]
392+
trace!("poll_once(): returning BlockNotInBestChain");
393+
324394
return Ok(PollResponse::BlockNotInBestChain);
325395
}
326396
next_hash
327397
} else {
328398
match last_res.nextblockhash {
329-
None => return Ok(PollResponse::NoMoreBlocks),
399+
None => {
400+
#[cfg(feature = "tracing-logs")]
401+
trace!("poll_once(): returning NoMoreBlocks");
402+
return Ok(PollResponse::NoMoreBlocks);
403+
}
330404
Some(next_hash) => next_hash,
331405
}
332406
};
333407

334408
let res = client.get_block_info(&next_hash)?;
335409
if res.confirmations < 0 {
410+
#[cfg(feature = "tracing-logs")]
411+
trace!("poll_once(): returning BlockNotInBestChain");
336412
return Ok(PollResponse::BlockNotInBestChain);
337413
}
338414

415+
#[cfg(feature = "tracing-logs")]
416+
trace!(
417+
"poll(): PollResponse::Block, height={}, hash={}",
418+
res.height,
419+
res.hash
420+
);
339421
return Ok(PollResponse::Block(res));
340422
}
341423

@@ -355,6 +437,12 @@ where
355437
};
356438

357439
// agreement point found
440+
#[cfg(feature = "tracing-logs")]
441+
trace!(
442+
"poll(): PollResponse::AgreementFound, height={}, hash={}",
443+
res.height,
444+
res.hash
445+
);
358446
return Ok(PollResponse::AgreementFound(res, cp));
359447
}
360448

crates/electrum/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ workspace = true
1616
bdk_core = { path = "../core", version = "0.6.0" }
1717
electrum-client = { version = "0.23.1", features = [ "proxy" ], default-features = false }
1818
serde_json = "1.0"
19+
tracing = { version = "0.1", optional = true }
1920

2021
[dev-dependencies]
2122
bdk_testenv = { path = "../testenv" }
@@ -27,6 +28,7 @@ default = ["use-rustls"]
2728
use-rustls = ["electrum-client/use-rustls"]
2829
use-rustls-ring = ["electrum-client/use-rustls-ring"]
2930
use-openssl = ["electrum-client/use-openssl"]
31+
tracing-logs = ["tracing"]
3032

3133
[[test]]
3234
name = "test_electrum"

crates/electrum/src/bdk_electrum_client.rs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ use bdk_core::{
99
use electrum_client::{ElectrumApi, Error, HeaderNotification};
1010
use std::sync::{Arc, Mutex};
1111

12+
#[cfg(feature = "tracing-logs")]
13+
use tracing::{debug, trace};
14+
1215
/// We include a chain suffix of a certain length for the purpose of robustness.
1316
const CHAIN_SUFFIX_LENGTH: u32 = 8;
1417

@@ -55,11 +58,16 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
5558
let tx_cache = self.tx_cache.lock().unwrap();
5659

5760
if let Some(tx) = tx_cache.get(&txid) {
61+
#[cfg(feature = "tracing-logs")]
62+
trace!(%txid, "hit tx cache");
5863
return Ok(Arc::clone(tx));
5964
}
6065

6166
drop(tx_cache);
6267

68+
#[cfg(feature = "tracing-logs")]
69+
trace!(%txid, "missed tx cache, fetching from electrum");
70+
6371
let tx = Arc::new(self.inner.transaction_get(&txid)?);
6472

6573
self.tx_cache.lock().unwrap().insert(txid, Arc::clone(&tx));
@@ -104,6 +112,9 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
104112
let mut request: FullScanRequest<K> = request.into();
105113
let start_time = request.start_time();
106114

115+
#[cfg(feature = "tracing-logs")]
116+
trace!("starting full_scan");
117+
107118
let tip_and_latest_blocks = match request.chain_tip() {
108119
Some(chain_tip) => Some(fetch_tip_and_latest_blocks(&self.inner, chain_tip)?),
109120
None => None,
@@ -187,6 +198,9 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
187198
let mut request: SyncRequest<I> = request.into();
188199
let start_time = request.start_time();
189200

201+
#[cfg(feature = "tracing-logs")]
202+
trace!("starting sync");
203+
190204
let tip_and_latest_blocks = match request.chain_tip() {
191205
Some(chain_tip) => Some(fetch_tip_and_latest_blocks(&self.inner, chain_tip)?),
192206
None => None,
@@ -266,10 +280,18 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
266280
let spks = (0..batch_size)
267281
.map_while(|_| spks_with_expected_txids.next())
268282
.collect::<Vec<_>>();
283+
#[cfg(feature = "tracing-logs")]
284+
trace!(batch_size = spks.len(), "fetching script history batch");
285+
269286
if spks.is_empty() {
287+
#[cfg(feature = "tracing-logs")]
288+
trace!("no script pubkeys left to scan in batch");
270289
return Ok(last_active_index);
271290
}
272291

292+
#[cfg(feature = "tracing-logs")]
293+
trace!("spk has tx history");
294+
273295
let spk_histories = self
274296
.inner
275297
.batch_script_get_history(spks.iter().map(|(_, s)| s.spk.as_script()))?;
@@ -278,7 +300,15 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
278300
if spk_history.is_empty() {
279301
match unused_spk_count.checked_add(1) {
280302
Some(i) if i < stop_gap => unused_spk_count = i,
281-
_ => return Ok(last_active_index),
303+
_ => {
304+
#[cfg(feature = "tracing-logs")]
305+
trace!(
306+
unused_spk_count,
307+
stop_gap,
308+
"gap limit reached, stopping scan for keychain"
309+
);
310+
return Ok(last_active_index);
311+
}
282312
};
283313
} else {
284314
last_active_index = Some(spk_index);
@@ -360,10 +390,14 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
360390

361391
if !has_residing && res.tx_hash == outpoint.txid {
362392
has_residing = true;
393+
#[cfg(feature = "tracing-logs")]
394+
trace!(%outpoint, "tx resides at outpoint, fetched and processed");
363395
tx_update.txs.push(Arc::clone(&tx));
364396
match res.height.try_into() {
365397
// Returned heights 0 & -1 are reserved for unconfirmed txs.
366398
Ok(height) if height > 0 => {
399+
#[cfg(feature = "tracing-logs")]
400+
trace!(%res.tx_hash, height, "added anchor from outpoint resolution");
367401
pending_anchors.push((res.tx_hash, height));
368402
}
369403
_ => {
@@ -379,13 +413,18 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
379413
.input
380414
.iter()
381415
.any(|txin| txin.previous_output == outpoint);
382-
if !has_spending {
416+
if has_spending {
417+
#[cfg(feature = "tracing-logs")]
418+
trace!(%outpoint, spending_txid = %res.tx_hash, "found tx spending from outpoint");
419+
} else {
383420
continue;
384421
}
385422
tx_update.txs.push(Arc::clone(&res_tx));
386423
match res.height.try_into() {
387424
// Returned heights 0 & -1 are reserved for unconfirmed txs.
388425
Ok(height) if height > 0 => {
426+
#[cfg(feature = "tracing-logs")]
427+
trace!(%res.tx_hash, height, "added anchor from outpoint resolution");
389428
pending_anchors.push((res.tx_hash, height));
390429
}
391430
_ => {
@@ -420,9 +459,13 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
420459
.expect("tx must have an output")
421460
.clone();
422461
txs.push((txid, tx));
462+
#[cfg(feature = "tracing-logs")]
463+
trace!(%txid, "fetched tx for txid, checking confirmation status");
423464
scripts.push(spk);
424465
}
425466
Err(electrum_client::Error::Protocol(_)) => {
467+
#[cfg(feature = "tracing-logs")]
468+
debug!(%txid, "electrum protocol error, skipping txid");
426469
continue;
427470
}
428471
Err(e) => return Err(e),
@@ -440,6 +483,8 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
440483
match res.height.try_into() {
441484
// Returned heights 0 & -1 are reserved for unconfirmed txs.
442485
Ok(height) if height > 0 => {
486+
#[cfg(feature = "tracing-logs")]
487+
trace!(%res.tx_hash, height, "anchor candidate found via txid history");
443488
pending_anchors.push((tx.0, height));
444489
}
445490
_ => {
@@ -499,8 +544,12 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
499544
let h = height as u32;
500545
let hash = height_to_hash[&h];
501546
if let Some(anchor) = anchor_cache.get(&(txid, hash)) {
547+
#[cfg(feature = "tracing-logs")]
548+
trace!(%txid, height, "anchor cache hit");
502549
results.push((txid, *anchor));
503550
} else {
551+
#[cfg(feature = "tracing-logs")]
552+
trace!(%txid, height, "anchor cache miss, will fetch merkle proof");
504553
to_fetch.push((txid, height, hash));
505554
}
506555
}
@@ -533,6 +582,8 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
533582
let mut valid =
534583
electrum_client::utils::validate_merkle_proof(&txid, &header.merkle_root, &proof);
535584
if !valid {
585+
#[cfg(feature = "tracing-logs")]
586+
debug!(%txid, height, "merkle validation failed, refetching header and retrying");
536587
header = self.inner.block_header(height)?;
537588
self.block_header_cache
538589
.lock()
@@ -543,6 +594,10 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
543594
&header.merkle_root,
544595
&proof,
545596
);
597+
if valid {
598+
#[cfg(feature = "tracing-logs")]
599+
trace!(%txid, height, "merkle proof validated after retry");
600+
}
546601
}
547602

548603
// Build and cache the anchor if merkle proof is valid.
@@ -558,6 +613,8 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
558613
.lock()
559614
.unwrap()
560615
.insert((txid, hash), anchor);
616+
#[cfg(feature = "tracing-logs")]
617+
trace!(%txid, height, %hash, "inserted anchor");
561618
results.push((txid, anchor));
562619
}
563620
}

0 commit comments

Comments
 (0)