Skip to main content

hotshot_events_service/
events_source.rs

1use std::{marker::PhantomData, sync::Arc};
2
3use async_broadcast::{InactiveReceiver, Sender as BroadcastSender, broadcast};
4use async_trait::async_trait;
5use futures::{
6    future::BoxFuture,
7    stream::{BoxStream, Stream, StreamExt},
8};
9use hotshot_types::{
10    PeerConfig,
11    event::{Event, EventType, LegacyEvent},
12    traits::node_implementation::NodeType,
13};
14use serde::{Deserialize, Serialize};
15use tide_disco::method::ReadState;
16const RETAINED_EVENTS_COUNT: usize = 4096;
17
18#[async_trait]
19pub trait EventsSource<Types>
20where
21    Types: NodeType,
22{
23    type EventStream: Stream<Item = Arc<Event<Types>>> + Unpin + Send + 'static;
24    type LegacyEventStream: Stream<Item = Arc<LegacyEvent<Types>>> + Unpin + Send + 'static;
25    async fn get_event_stream(&self, filter: Option<EventFilterSet<Types>>) -> Self::EventStream;
26    async fn get_legacy_event_stream(
27        &self,
28        filter: Option<EventFilterSet<Types>>,
29    ) -> Self::LegacyEventStream;
30    async fn get_startup_info(&self) -> StartupInfo<Types>;
31}
32
33#[derive(Clone, Debug, Serialize, Deserialize)]
34#[serde(bound = "Types::SignatureKey: for<'a> Deserialize<'a>")]
35pub struct StartupInfo<Types: NodeType> {
36    pub known_node_with_stake: Vec<PeerConfig<Types>>,
37    pub non_staked_node_count: usize,
38}
39
40#[async_trait]
41pub trait EventConsumer<Types>
42where
43    Types: NodeType,
44{
45    async fn handle_event(&mut self, event: Event<Types>);
46}
47
48#[derive(Debug)]
49pub struct EventsStreamer<Types: NodeType> {
50    // required for api subscription
51    inactive_to_subscribe_clone_recv: InactiveReceiver<Arc<Event<Types>>>,
52    subscriber_send_channel: BroadcastSender<Arc<Event<Types>>>,
53
54    // required for sending startup info
55    known_nodes_with_stake: Vec<PeerConfig<Types>>,
56    non_staked_node_count: usize,
57}
58
59impl<Types: NodeType> EventsStreamer<Types> {
60    pub fn known_node_with_stake(&self) -> Vec<PeerConfig<Types>> {
61        self.known_nodes_with_stake.clone()
62    }
63
64    pub fn non_staked_node_count(&self) -> usize {
65        self.non_staked_node_count
66    }
67
68    /// Number of currently registered event subscribers. Subscribers are registered
69    /// asynchronously after a client's subscribe call returns; events broadcast before
70    /// registration are not replayed. Lets callers wait for registration before publishing.
71    pub fn subscriber_count(&self) -> usize {
72        self.subscriber_send_channel.receiver_count()
73    }
74}
75
76#[async_trait]
77impl<Types: NodeType> EventConsumer<Types> for EventsStreamer<Types> {
78    async fn handle_event(&mut self, event: Event<Types>) {
79        if let Err(e) = self.subscriber_send_channel.broadcast(event.into()).await {
80            tracing::debug!("Error broadcasting the event: {e:?}");
81        }
82    }
83}
84
85/// Wrapper struct representing a set of event filters.
86#[derive(Clone, Debug)]
87pub struct EventFilterSet<Types: NodeType>(pub(crate) Vec<EventFilter<Types>>);
88
89/// `From` trait impl to create an `EventFilterSet` from a vector of `EventFilter`s.
90impl<Types: NodeType> From<Vec<EventFilter<Types>>> for EventFilterSet<Types> {
91    fn from(filters: Vec<EventFilter<Types>>) -> Self {
92        EventFilterSet(filters)
93    }
94}
95
96/// `From` trait impl to create an `EventFilterSet` from a single `EventFilter`.
97impl<Types: NodeType> From<EventFilter<Types>> for EventFilterSet<Types> {
98    fn from(filter: EventFilter<Types>) -> Self {
99        EventFilterSet(vec![filter])
100    }
101}
102
103impl<Types: NodeType> EventFilterSet<Types> {
104    /// Determines whether the given hotshot event should be broadcast based on the filters in the set.
105    ///
106    ///  Returns `true` if the event should be broadcast, `false` otherwise.
107    pub(crate) fn should_broadcast(&self, hotshot_event: &EventType<Types>) -> bool {
108        let filter = &self.0;
109
110        match hotshot_event {
111            EventType::Error { .. } => filter.contains(&EventFilter::Error),
112            EventType::Decide { .. } => filter.contains(&EventFilter::Decide),
113            EventType::ReplicaViewTimeout { .. } => {
114                filter.contains(&EventFilter::ReplicaViewTimeout)
115            },
116            EventType::ViewFinished { .. } => filter.contains(&EventFilter::ViewFinished),
117            EventType::ViewTimeout { .. } => filter.contains(&EventFilter::ViewTimeout),
118            EventType::Transactions { .. } => filter.contains(&EventFilter::Transactions),
119            EventType::DaProposal { .. } => filter.contains(&EventFilter::DaProposal),
120            EventType::QuorumProposal { .. } => filter.contains(&EventFilter::QuorumProposal),
121            EventType::UpgradeProposal { .. } => filter.contains(&EventFilter::UpgradeProposal),
122            _ => false,
123        }
124    }
125}
126
127/// Possible event filters
128/// If the hotshot`EventType` enum is modified, this enum should also be updated
129#[derive(Clone, Debug, PartialEq)]
130pub enum EventFilter<Types: NodeType> {
131    Error,
132    Decide,
133    ReplicaViewTimeout,
134    ViewFinished,
135    ViewTimeout,
136    Transactions,
137    DaProposal,
138    QuorumProposal,
139    UpgradeProposal,
140    Pd(PhantomData<Types>),
141}
142
143#[async_trait]
144impl<Types: NodeType> EventsSource<Types> for EventsStreamer<Types> {
145    type EventStream = BoxStream<'static, Arc<Event<Types>>>;
146    type LegacyEventStream = BoxStream<'static, Arc<LegacyEvent<Types>>>;
147
148    async fn get_event_stream(&self, filter: Option<EventFilterSet<Types>>) -> Self::EventStream {
149        let receiver = self.inactive_to_subscribe_clone_recv.activate_cloned();
150
151        if let Some(filter) = filter {
152            receiver
153                .filter(move |event| {
154                    futures::future::ready(filter.should_broadcast(&event.as_ref().event))
155                })
156                .boxed()
157        } else {
158            receiver.boxed()
159        }
160    }
161
162    async fn get_legacy_event_stream(
163        &self,
164        filter: Option<EventFilterSet<Types>>,
165    ) -> Self::LegacyEventStream {
166        let receiver = self.inactive_to_subscribe_clone_recv.activate_cloned();
167
168        if let Some(filter) = filter {
169            receiver
170                .filter(move |event| {
171                    futures::future::ready(filter.should_broadcast(&event.as_ref().event))
172                })
173                .filter_map(|a| {
174                    futures::future::ready(Event::to_legacy(a.as_ref().clone()).ok().map(Arc::new))
175                })
176                .boxed()
177        } else {
178            receiver
179                .filter_map(|a| {
180                    futures::future::ready(Event::to_legacy(a.as_ref().clone()).ok().map(Arc::new))
181                })
182                .boxed()
183        }
184    }
185
186    async fn get_startup_info(&self) -> StartupInfo<Types> {
187        StartupInfo {
188            known_node_with_stake: self.known_node_with_stake(),
189            non_staked_node_count: self.non_staked_node_count(),
190        }
191    }
192}
193
194impl<Types: NodeType> EventsStreamer<Types> {
195    pub fn new(
196        known_nodes_with_stake: Vec<PeerConfig<Types>>,
197        non_staked_node_count: usize,
198    ) -> Self {
199        let (mut subscriber_send_channel, to_subscribe_clone_recv) =
200            broadcast::<Arc<Event<Types>>>(RETAINED_EVENTS_COUNT);
201        // set the overflow to true to drop older messages from the channel
202        subscriber_send_channel.set_overflow(true);
203        // set the await active to false to not block the sender
204        subscriber_send_channel.set_await_active(false);
205        let inactive_to_subscribe_clone_recv = to_subscribe_clone_recv.deactivate();
206
207        EventsStreamer {
208            subscriber_send_channel,
209            inactive_to_subscribe_clone_recv,
210            known_nodes_with_stake,
211            non_staked_node_count,
212        }
213    }
214}
215
216#[async_trait]
217impl<Types: NodeType> ReadState for EventsStreamer<Types> {
218    type State = Self;
219
220    async fn read<T>(
221        &self,
222        op: impl Send + for<'a> FnOnce(&'a Self::State) -> BoxFuture<'a, T> + 'async_trait,
223    ) -> T {
224        op(self).await
225    }
226}