1use alloy_consensus::Header;
4use futures::Stream;
5use reth_storage_api::CanonChainTracker;
6use std::{
7 fmt,
8 pin::Pin,
9 task::{ready, Context, Poll},
10 time::Duration,
11};
12use tokio::time::{Instant, Interval};
13
14const CHECK_INTERVAL: Duration = Duration::from_secs(300);
16const NO_TRANSITION_CONFIG_EXCHANGED_PERIOD: Duration = Duration::from_secs(120);
19const NO_FORKCHOICE_UPDATE_RECEIVED_PERIOD: Duration = Duration::from_secs(120);
22
23pub struct ConsensusLayerHealthEvents<H = Header> {
25 interval: Interval,
26 canon_chain: Box<dyn CanonChainTracker<Header = H>>,
27}
28
29impl fmt::Debug for ConsensusLayerHealthEvents {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 f.debug_struct("ConsensusLayerHealthEvents").field("interval", &self.interval).finish()
32 }
33}
34
35impl<H> ConsensusLayerHealthEvents<H> {
36 pub fn new(canon_chain: Box<dyn CanonChainTracker<Header = H>>) -> Self {
38 let interval = tokio::time::interval_at(Instant::now() + CHECK_INTERVAL, CHECK_INTERVAL);
40 Self { interval, canon_chain }
41 }
42}
43
44impl Stream for ConsensusLayerHealthEvents {
45 type Item = ConsensusLayerHealthEvent;
46
47 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
48 let this = self.get_mut();
49
50 loop {
51 ready!(this.interval.poll_tick(cx));
52
53 if let Some(fork_choice) = this.canon_chain.last_received_update_timestamp() {
54 if fork_choice.elapsed() <= NO_FORKCHOICE_UPDATE_RECEIVED_PERIOD {
55 continue
57 }
58 return Poll::Ready(Some(
60 ConsensusLayerHealthEvent::HaveNotReceivedUpdatesForAWhile(
61 fork_choice.elapsed(),
62 ),
63 ))
64 }
65
66 if let Some(transition_config) =
67 this.canon_chain.last_exchanged_transition_configuration_timestamp()
68 {
69 return if transition_config.elapsed() <= NO_TRANSITION_CONFIG_EXCHANGED_PERIOD {
70 Poll::Ready(Some(ConsensusLayerHealthEvent::NeverReceivedUpdates))
72 } else {
73 Poll::Ready(Some(ConsensusLayerHealthEvent::HasNotBeenSeenForAWhile(
75 transition_config.elapsed(),
76 )))
77 }
78 }
79
80 return Poll::Ready(Some(ConsensusLayerHealthEvent::NeverSeen))
82 }
83 }
84}
85
86#[derive(Clone, Copy, Debug)]
89pub enum ConsensusLayerHealthEvent {
90 NeverSeen,
92 HasNotBeenSeenForAWhile(Duration),
94 NeverReceivedUpdates,
96 HaveNotReceivedUpdatesForAWhile(Duration),
98}