@@ -877,44 +877,66 @@ impl CbfChainSource {
877877
878878 /// Derive per-target fee rates from recent blocks' coinbase outputs.
879879 ///
880- /// Returns `Ok(None)` when no chain tip is available yet (first startup before sync).
880+ /// On the very first startup call this waits for the initial `FiltersSynced` event so
881+ /// the fee cache gets populated before the background loop's first tick; subsequent
882+ /// calls find the tip already set and proceed immediately. The wait shares the
883+ /// overall timeout budget with the block fetches below.
884+ ///
885+ /// Returns `Ok(None)` when no chain tip can be established within the budget, or when
886+ /// the tip block itself cannot be served by the CBF peer (initial sync / reorg). When
887+ /// subsequent historical blocks are unavailable (Kyoto only serves blocks in its
888+ /// known header chain range, so walking back far enough hits `UnknownHash`), the
889+ /// function falls back to whatever blocks it already fetched rather than failing the
890+ /// whole fee update.
881891 async fn fee_rate_cache_from_cbf (
882892 & self ,
883893 ) -> Result < Option < HashMap < crate :: fee_estimator:: ConfirmationTarget , FeeRate > > , Error > {
884894 let requester = self . requester ( ) ?;
885895
886- let tip_hash = match * self . latest_tip . lock ( ) . unwrap ( ) {
887- Some ( hash) => hash,
888- None => {
889- log_debug ! ( self . logger, "No tip available yet for fee rate estimation, skipping." ) ;
896+ let now = Instant :: now ( ) ;
897+ let timeout = Duration :: from_secs (
898+ self . sync_config . timeouts_config . fee_rate_cache_update_timeout_secs ,
899+ ) ;
900+
901+ let tip_hash = loop {
902+ if let Some ( hash) = * self . latest_tip . lock ( ) . unwrap ( ) {
903+ break hash;
904+ }
905+ if now. elapsed ( ) >= timeout {
906+ log_debug ! (
907+ self . logger,
908+ "No CBF tip available within timeout; skipping fee update." ,
909+ ) ;
890910 return Ok ( None ) ;
891- } ,
911+ }
912+ tokio:: time:: sleep ( Duration :: from_millis ( 100 ) ) . await ;
892913 } ;
893914
894- let now = Instant :: now ( ) ;
895-
896915 // Fetch fee rates from the last N blocks for per-target estimation.
897916 // We compute fee rates ourselves rather than using Requester::average_fee_rate,
898917 // so we can sample multiple blocks and select percentiles per confirmation target.
899918 let mut block_fee_rates: Vec < u64 > = Vec :: with_capacity ( FEE_RATE_LOOKBACK_BLOCKS ) ;
900919 let mut current_hash = tip_hash;
901920
902- let timeout = Duration :: from_secs (
903- self . sync_config . timeouts_config . fee_rate_cache_update_timeout_secs ,
904- ) ;
905- let fetch_start = Instant :: now ( ) ;
921+ let fetch_start = now;
906922
907923 for idx in 0 ..FEE_RATE_LOOKBACK_BLOCKS {
908924 // Check if we've exceeded the overall timeout for fee estimation.
909925 let remaining_timeout = timeout. saturating_sub ( fetch_start. elapsed ( ) ) ;
910926 if remaining_timeout. is_zero ( ) {
927+ if !block_fee_rates. is_empty ( ) {
928+ break ;
929+ }
911930 log_error ! ( self . logger, "Updating fee rate estimates timed out." ) ;
912931 return Err ( Error :: FeerateEstimationUpdateTimeout ) ;
913932 }
914933
915934 // Fetch the block via P2P. On the first iteration, a fetch failure
916935 // likely means the cached tip is stale (initial sync or reorg), so
917936 // we clear the tip and skip gracefully instead of returning an error.
937+ // On later iterations we may walk past Kyoto's known header range and
938+ // hit `UnknownHash`; in that case we use whatever blocks we already
939+ // have rather than failing the whole fee update.
918940 let indexed_block =
919941 match tokio:: time:: timeout ( remaining_timeout, requester. get_block ( current_hash) )
920942 . await
@@ -932,12 +954,14 @@ impl CbfChainSource {
932954 return Ok ( None ) ;
933955 } ,
934956 Ok ( Err ( e) ) => {
935- log_error ! (
957+ log_debug ! (
936958 self . logger,
937- "Failed to fetch block for fee estimation: {:?}" ,
959+ "CBF could not serve block {} during fee estimation after {} successful fetches: {:?}" ,
960+ current_hash,
961+ block_fee_rates. len( ) ,
938962 e
939963 ) ;
940- return Err ( Error :: FeerateEstimationUpdateFailed ) ;
964+ break ;
941965 } ,
942966 Err ( e) if idx == 0 => {
943967 log_debug ! (
@@ -951,8 +975,14 @@ impl CbfChainSource {
951975 return Ok ( None ) ;
952976 } ,
953977 Err ( e) => {
954- log_error ! ( self . logger, "Updating fee rate estimates timed out: {}" , e) ;
955- return Err ( Error :: FeerateEstimationUpdateTimeout ) ;
978+ log_debug ! (
979+ self . logger,
980+ "Timed out fetching block {} during fee estimation after {} successful fetches: {}" ,
981+ current_hash,
982+ block_fee_rates. len( ) ,
983+ e
984+ ) ;
985+ break ;
956986 } ,
957987 } ;
958988
0 commit comments