Skip to content

Commit cab3fb8

Browse files
committed
fix(cbf): stamp sync timestamps even when no scripts are registered
Both sync_onchain_wallet and sync_lightning_wallet returned Ok(()) early when there were no registered scripts, *before* reaching the update_node_metrics_timestamp call. As a result, on-chain-only nodes (no Lightning channels) never advanced latest_lightning_wallet_sync_ timestamp, and any caller treating that timestamp as a "sync ran" signal would observe a permanent stall. This is the root cause behind the wait_for_cbf_sync helper timing out in CI for every test that doesn't open a Lightning channel (fee_rate_estimation_after_manual_sync_cbf, onchain_send_receive_cbf, onchain_wallet_recovery_cbf, repeated_manual_sync_cbf, start_stop_reinit_cbf). The helper requires both onchain and lightning timestamps to advance, and the lightning side could never advance for those nodes. Other chain sources (electrum, bitcoind) already stamp the lightning sync timestamp unconditionally on success, so this brings CBF in line with them: an empty-scripts call is treated as a successful no-op sync, the actual filter scan is still correctly skipped, and the requester() precondition check remains in place to ensure we never mark a sync as successful while the chain source isn't running. Reviewed by Codex (gpt-5.4). AI-assisted with Claude Code.
1 parent ead0e2a commit cab3fb8

1 file changed

Lines changed: 63 additions & 65 deletions

File tree

src/chain/cbf.rs

Lines changed: 63 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -586,53 +586,52 @@ impl CbfChainSource {
586586

587587
let res = async {
588588
let requester = self.requester()?;
589-
let now = Instant::now();
590589

591590
let scripts = onchain_wallet.get_spks_for_cbf_sync(BDK_CLIENT_STOP_GAP);
592591
if scripts.is_empty() {
593592
log_debug!(self.logger, "No wallet scripts to sync via CBF.");
594-
return Ok(());
595-
}
596-
597-
let timeout_fut = tokio::time::timeout(
598-
Duration::from_secs(
599-
self.sync_config.timeouts_config.onchain_wallet_sync_timeout_secs,
600-
),
601-
self.sync_onchain_wallet_op(requester, &onchain_wallet, scripts),
602-
);
593+
} else {
594+
let now = Instant::now();
595+
let timeout_fut = tokio::time::timeout(
596+
Duration::from_secs(
597+
self.sync_config.timeouts_config.onchain_wallet_sync_timeout_secs,
598+
),
599+
self.sync_onchain_wallet_op(requester, &onchain_wallet, scripts),
600+
);
603601

604-
let (tx_update, sync_update) = match timeout_fut.await {
605-
Ok(res) => res?,
606-
Err(e) => {
607-
log_error!(self.logger, "Sync of on-chain wallet timed out: {}", e);
608-
return Err(Error::WalletOperationTimeout);
609-
},
610-
};
602+
let (tx_update, sync_update) = match timeout_fut.await {
603+
Ok(res) => res?,
604+
Err(e) => {
605+
log_error!(self.logger, "Sync of on-chain wallet timed out: {}", e);
606+
return Err(Error::WalletOperationTimeout);
607+
},
608+
};
611609

612-
// Build chain checkpoint extending from the wallet's current tip.
613-
let mut cp = onchain_wallet.latest_checkpoint();
614-
for (height, header) in sync_update.recent_history() {
615-
if *height > cp.height() {
616-
let block_id = BlockId { height: *height, hash: header.block_hash() };
617-
cp = cp.push(block_id).unwrap_or_else(|old| old);
610+
// Build chain checkpoint extending from the wallet's current tip.
611+
let mut cp = onchain_wallet.latest_checkpoint();
612+
for (height, header) in sync_update.recent_history() {
613+
if *height > cp.height() {
614+
let block_id = BlockId { height: *height, hash: header.block_hash() };
615+
cp = cp.push(block_id).unwrap_or_else(|old| old);
616+
}
617+
}
618+
let tip = sync_update.tip();
619+
if tip.height > cp.height() {
620+
let tip_block_id = BlockId { height: tip.height, hash: tip.hash };
621+
cp = cp.push(tip_block_id).unwrap_or_else(|old| old);
618622
}
619-
}
620-
let tip = sync_update.tip();
621-
if tip.height > cp.height() {
622-
let tip_block_id = BlockId { height: tip.height, hash: tip.hash };
623-
cp = cp.push(tip_block_id).unwrap_or_else(|old| old);
624-
}
625623

626-
let update =
627-
Update { last_active_indices: BTreeMap::new(), tx_update, chain: Some(cp) };
624+
let update =
625+
Update { last_active_indices: BTreeMap::new(), tx_update, chain: Some(cp) };
628626

629-
onchain_wallet.apply_update(update)?;
627+
onchain_wallet.apply_update(update)?;
630628

631-
log_debug!(
632-
self.logger,
633-
"Sync of on-chain wallet via CBF finished in {}ms.",
634-
now.elapsed().as_millis()
635-
);
629+
log_debug!(
630+
self.logger,
631+
"Sync of on-chain wallet via CBF finished in {}ms.",
632+
now.elapsed().as_millis()
633+
);
634+
}
636635

637636
update_node_metrics_timestamp(
638637
&self.node_metrics,
@@ -733,40 +732,39 @@ impl CbfChainSource {
733732

734733
let res = async {
735734
let requester = self.requester()?;
736-
let now = Instant::now();
737735

738736
let scripts: Vec<ScriptBuf> = self.registered_scripts.lock().unwrap().clone();
739737
if scripts.is_empty() {
740738
log_debug!(self.logger, "No registered scripts for CBF lightning sync.");
741-
return Ok(());
742-
}
743-
744-
let timeout_fut = tokio::time::timeout(
745-
Duration::from_secs(
746-
self.sync_config.timeouts_config.lightning_wallet_sync_timeout_secs,
747-
),
748-
self.sync_lightning_wallet_op(
749-
requester,
750-
channel_manager,
751-
chain_monitor,
752-
output_sweeper,
753-
scripts,
754-
),
755-
);
739+
} else {
740+
let now = Instant::now();
741+
let timeout_fut = tokio::time::timeout(
742+
Duration::from_secs(
743+
self.sync_config.timeouts_config.lightning_wallet_sync_timeout_secs,
744+
),
745+
self.sync_lightning_wallet_op(
746+
requester,
747+
channel_manager,
748+
chain_monitor,
749+
output_sweeper,
750+
scripts,
751+
),
752+
);
756753

757-
match timeout_fut.await {
758-
Ok(res) => res?,
759-
Err(e) => {
760-
log_error!(self.logger, "Sync of Lightning wallet timed out: {}", e);
761-
return Err(Error::TxSyncTimeout);
762-
},
763-
};
754+
match timeout_fut.await {
755+
Ok(res) => res?,
756+
Err(e) => {
757+
log_error!(self.logger, "Sync of Lightning wallet timed out: {}", e);
758+
return Err(Error::TxSyncTimeout);
759+
},
760+
};
764761

765-
log_debug!(
766-
self.logger,
767-
"Sync of Lightning wallet via CBF finished in {}ms.",
768-
now.elapsed().as_millis()
769-
);
762+
log_debug!(
763+
self.logger,
764+
"Sync of Lightning wallet via CBF finished in {}ms.",
765+
now.elapsed().as_millis()
766+
);
767+
}
770768

771769
update_node_metrics_timestamp(
772770
&self.node_metrics,

0 commit comments

Comments
 (0)