Skip to content

Commit a70d6ec

Browse files
committed
remove latest_tip from the cbf chain source
1 parent 8261e32 commit a70d6ec

1 file changed

Lines changed: 35 additions & 46 deletions

File tree

src/chain/cbf.rs

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,6 @@ pub(super) struct CbfChainSource {
8080
fee_source: FeeSource,
8181
/// Tracks whether the bip157 node is running and holds the command handle.
8282
cbf_runtime_status: Arc<Mutex<CbfRuntimeStatus>>,
83-
/// Latest chain tip hash, updated by the background event processing task.
84-
latest_tip: Arc<Mutex<Option<BlockHash>>>,
8583
/// Scripts to match against compact block filters during a scan.
8684
watched_scripts: Arc<RwLock<Vec<ScriptBuf>>>,
8785
/// Block (height, hash) pairs where filters matched watched scripts.
@@ -119,7 +117,6 @@ enum CbfRuntimeStatus {
119117

120118
/// Shared state passed to the background event processing task.
121119
struct CbfEventState {
122-
latest_tip: Arc<Mutex<Option<BlockHash>>>,
123120
watched_scripts: Arc<RwLock<Vec<ScriptBuf>>>,
124121
matched_block_hashes: Arc<Mutex<Vec<(u32, BlockHash)>>>,
125122
sync_completion_tx: Arc<Mutex<Option<oneshot::Sender<SyncUpdate>>>>,
@@ -148,7 +145,6 @@ impl CbfChainSource {
148145
};
149146

150147
let cbf_runtime_status = Arc::new(Mutex::new(CbfRuntimeStatus::Stopped));
151-
let latest_tip = Arc::new(Mutex::new(None));
152148
let watched_scripts = Arc::new(RwLock::new(Vec::new()));
153149
let matched_block_hashes = Arc::new(Mutex::new(Vec::new()));
154150
let sync_completion_tx = Arc::new(Mutex::new(None));
@@ -163,7 +159,6 @@ impl CbfChainSource {
163159
sync_config,
164160
fee_source,
165161
cbf_runtime_status,
166-
latest_tip,
167162
watched_scripts,
168163
matched_block_hashes,
169164
sync_completion_tx,
@@ -288,7 +283,6 @@ impl CbfChainSource {
288283
// block is 'static (no borrows of `self`).
289284
let restart_status = Arc::clone(&self.cbf_runtime_status);
290285
let restart_logger = Arc::clone(&self.logger);
291-
let restart_latest_tip = Arc::clone(&self.latest_tip);
292286
let restart_watched_scripts = Arc::clone(&self.watched_scripts);
293287
let restart_matched_block_hashes = Arc::clone(&self.matched_block_hashes);
294288
let restart_sync_completion_tx = Arc::clone(&self.sync_completion_tx);
@@ -317,7 +311,6 @@ impl CbfChainSource {
317311
Arc::clone(&restart_logger),
318312
));
319313
let event_state = CbfEventState {
320-
latest_tip: Arc::clone(&restart_latest_tip),
321314
watched_scripts: Arc::clone(&restart_watched_scripts),
322315
matched_block_hashes: Arc::clone(&restart_matched_block_hashes),
323316
sync_completion_tx: Arc::clone(&restart_sync_completion_tx),
@@ -428,7 +421,6 @@ impl CbfChainSource {
428421
match event {
429422
Event::FiltersSynced(sync_update) => {
430423
let tip = sync_update.tip();
431-
*state.latest_tip.lock().unwrap() = Some(tip.hash);
432424
log_info!(
433425
logger,
434426
"CBF filters synced to tip: height={}, hash={}",
@@ -448,7 +440,6 @@ impl CbfChainSource {
448440
reorganized.len(),
449441
accepted.len(),
450442
);
451-
*state.latest_tip.lock().unwrap() = None;
452443

453444
// No height reset needed: skip heights are derived from
454445
// BDK's checkpoint (on-chain) and LDK's best block
@@ -861,60 +852,69 @@ impl CbfChainSource {
861852

862853
/// Derive per-target fee rates from recent blocks' coinbase outputs.
863854
///
864-
/// Returns `Ok(None)` when no chain tip is available yet (first startup before sync).
855+
/// Returns `Ok(None)` when the chain is too short to sample `FEE_RATE_LOOKBACK_BLOCKS`
856+
/// blocks (e.g. kyoto has not yet synced past the genesis region).
865857
async fn fee_rate_cache_from_cbf(
866858
&self,
867859
) -> Result<Option<HashMap<crate::fee_estimator::ConfirmationTarget, FeeRate>>, Error> {
868860
let requester = self.requester()?;
869861

870-
let tip_hash = match *self.latest_tip.lock().unwrap() {
871-
Some(hash) => hash,
872-
None => {
873-
log_debug!(self.logger, "No tip available yet for fee rate estimation, skipping.");
862+
let timeout = Duration::from_secs(
863+
self.sync_config.timeouts_config.fee_rate_cache_update_timeout_secs,
864+
);
865+
let fetch_start = Instant::now();
866+
867+
// Ask kyoto for its current chain tip rather than maintaining a mirrored
868+
// cache: the returned hash is always fresh (post-reorg, post-restart),
869+
// so no defensive invalidation is needed below.
870+
let tip = match tokio::time::timeout(timeout, requester.chain_tip()).await {
871+
Ok(Ok(tip)) => tip,
872+
Ok(Err(e)) => {
873+
log_debug!(
874+
self.logger,
875+
"Failed to fetch CBF chain tip for fee estimation: {:?}",
876+
e,
877+
);
874878
return Ok(None);
875879
},
880+
Err(e) => {
881+
log_error!(self.logger, "Timed out fetching CBF chain tip: {}", e);
882+
return Err(Error::FeerateEstimationUpdateTimeout);
883+
},
876884
};
877885

886+
if (tip.height as usize) < FEE_RATE_LOOKBACK_BLOCKS {
887+
log_debug!(
888+
self.logger,
889+
"CBF chain tip at height {} is below the {}-block lookback window, \
890+
skipping fee estimation.",
891+
tip.height,
892+
FEE_RATE_LOOKBACK_BLOCKS,
893+
);
894+
return Ok(None);
895+
}
896+
878897
let now = Instant::now();
879898

880899
// Fetch fee rates from the last N blocks for per-target estimation.
881900
// We compute fee rates ourselves rather than using Requester::average_fee_rate,
882901
// so we can sample multiple blocks and select percentiles per confirmation target.
883902
let mut block_fee_rates: Vec<u64> = Vec::with_capacity(FEE_RATE_LOOKBACK_BLOCKS);
884-
let mut current_hash = tip_hash;
903+
let mut current_hash = tip.hash;
885904

886-
let timeout = Duration::from_secs(
887-
self.sync_config.timeouts_config.fee_rate_cache_update_timeout_secs,
888-
);
889-
let fetch_start = Instant::now();
890-
891-
for idx in 0..FEE_RATE_LOOKBACK_BLOCKS {
905+
for _ in 0..FEE_RATE_LOOKBACK_BLOCKS {
892906
// Check if we've exceeded the overall timeout for fee estimation.
893907
let remaining_timeout = timeout.saturating_sub(fetch_start.elapsed());
894908
if remaining_timeout.is_zero() {
895909
log_error!(self.logger, "Updating fee rate estimates timed out.");
896910
return Err(Error::FeerateEstimationUpdateTimeout);
897911
}
898912

899-
// Fetch the block via P2P. On the first iteration, a fetch failure
900-
// likely means the cached tip is stale (initial sync or reorg), so
901-
// we clear the tip and skip gracefully instead of returning an error.
902913
let indexed_block =
903914
match tokio::time::timeout(remaining_timeout, requester.get_block(current_hash))
904915
.await
905916
{
906917
Ok(Ok(indexed_block)) => indexed_block,
907-
Ok(Err(e)) if idx == 0 => {
908-
log_debug!(
909-
self.logger,
910-
"Cached CBF tip {} was unavailable during fee estimation, \
911-
likely due to initial sync or a reorg: {:?}",
912-
current_hash,
913-
e
914-
);
915-
*self.latest_tip.lock().unwrap() = None;
916-
return Ok(None);
917-
},
918918
Ok(Err(e)) => {
919919
log_error!(
920920
self.logger,
@@ -923,17 +923,6 @@ impl CbfChainSource {
923923
);
924924
return Err(Error::FeerateEstimationUpdateFailed);
925925
},
926-
Err(e) if idx == 0 => {
927-
log_debug!(
928-
self.logger,
929-
"Timed out fetching cached CBF tip {} during fee estimation, \
930-
likely due to initial sync or a reorg: {}",
931-
current_hash,
932-
e
933-
);
934-
*self.latest_tip.lock().unwrap() = None;
935-
return Ok(None);
936-
},
937926
Err(e) => {
938927
log_error!(self.logger, "Updating fee rate estimates timed out: {}", e);
939928
return Err(Error::FeerateEstimationUpdateTimeout);

0 commit comments

Comments
 (0)