Skip to main content

espresso_node/
external_event_handler.rs

1//! Should probably rename this to "external" or something
2
3use std::sync::Arc;
4
5use anyhow::{Context, Result, bail};
6use either::Either;
7use espresso_types::{PubKey, SeqTypes, v0::traits::SequencerPersistence};
8use hotshot::types::Message;
9use hotshot_new_protocol::client::ClientApi;
10use hotshot_types::{
11    message::MessageKind,
12    traits::network::{BroadcastDelay, ConnectedNetwork, Topic, ViewMessage},
13};
14use request_response::network::Bytes;
15use serde::{Deserialize, Serialize};
16use tokio::sync::mpsc::{Receiver, Sender, error::TrySendError};
17use vbs::{BinarySerializer, bincode_serializer::BincodeSerializer, version::StaticVersion};
18
19use crate::{
20    consensus_handle::ConsensusHandle,
21    context::{ConsensusNode, TaskList},
22};
23
24/// An external message that can be sent to or received from a node
25#[derive(Debug, Serialize, Deserialize, Clone)]
26pub enum ExternalMessage {
27    RequestResponse(#[serde(with = "serde_bytes")] Vec<u8>),
28}
29
30/// The external event handler
31#[derive(Clone)]
32pub struct ExternalEventHandler {
33    /// The sender to the request-response protocol
34    request_response_sender: Sender<Bytes>,
35}
36
37// The different types of outbound messages (broadcast or direct)
38#[derive(Debug)]
39#[allow(dead_code)]
40pub enum OutboundMessage {
41    Direct(MessageKind<SeqTypes>, PubKey),
42    Broadcast(MessageKind<SeqTypes>),
43}
44
45impl ExternalEventHandler {
46    /// Creates a new `ExternalEventHandler` with the given network
47    pub async fn new<N, P>(
48        tasks: &mut TaskList,
49        request_response_sender: Sender<Bytes>,
50        outbound_message_receiver: Receiver<OutboundMessage>,
51        consensus_handle: Arc<ConsensusHandle<SeqTypes, ConsensusNode<N, P>>>,
52        network: Arc<N>,
53        public_key: PubKey,
54    ) -> Result<Self>
55    where
56        N: ConnectedNetwork<PubKey>,
57        P: SequencerPersistence,
58    {
59        // Spawn the outbound message handling loop
60        tasks.spawn(
61            "ExternalEventHandler",
62            Self::outbound_message_loop(
63                outbound_message_receiver,
64                consensus_handle,
65                network,
66                public_key,
67            ),
68        );
69
70        Ok(Self {
71            request_response_sender,
72        })
73    }
74
75    /// Handles an event
76    ///
77    /// # Errors
78    /// If the message type is unknown or if there is an error serializing or deserializing the message
79    pub async fn handle_event(&self, external_message_bytes: &[u8]) -> Result<()> {
80        // Deserialize the external message
81        let external_message = bincode::deserialize(external_message_bytes)
82            .with_context(|| "Failed to deserialize external message")?;
83
84        // Match the type
85        match external_message {
86            ExternalMessage::RequestResponse(request_response) => {
87                match self
88                    .request_response_sender
89                    .try_send(request_response.into())
90                {
91                    Ok(()) => Ok(()),
92                    Err(TrySendError::Full(..)) => bail!("request-response channel full"),
93                    Err(TrySendError::Closed(..)) => bail!("request-response channel closed"),
94                }
95            },
96        }
97    }
98
99    /// The main loop for sending outbound messages.
100    async fn outbound_message_loop<N, P>(
101        mut receiver: Receiver<OutboundMessage>,
102        consensus_handle: Arc<ConsensusHandle<SeqTypes, ConsensusNode<N, P>>>,
103        network: Arc<N>,
104        public_key: PubKey,
105    ) where
106        N: ConnectedNetwork<PubKey>,
107        P: SequencerPersistence,
108    {
109        let mut network = Either::Left(network);
110
111        while let Some(message) = receiver.recv().await {
112            // Once the coordinator is running it owns the only live network;
113            // route external messages through it. The coordinator never
114            // stops once started, so swap the legacy network out for its
115            // client API for good.
116            if network.is_left()
117                && let Some(client_api) = consensus_handle.client_api().await
118            {
119                network = Either::Right(client_api);
120            }
121            let network = match &network {
122                Either::Right(client_api) => {
123                    Self::send_via_coordinator(client_api, message, public_key).await;
124                    continue;
125                },
126                Either::Left(network) => network,
127            };
128
129            // Match the message type
130            match message {
131                OutboundMessage::Direct(message, recipient) => {
132                    let view = message.view_number();
133                    // Wrap it in the real message type
134                    let message_inner = Message {
135                        sender: public_key,
136                        kind: message,
137                    };
138
139                    // Serialize it
140                    let message_bytes =
141                        match BincodeSerializer::<StaticVersion<0, 0>>::serialize(&message_inner) {
142                            Ok(message_bytes) => message_bytes,
143                            Err(err) => {
144                                tracing::warn!("Failed to serialize direct message: {}", err);
145                                continue;
146                            },
147                        };
148
149                    // Send the message to the recipient
150                    let network = Arc::clone(network);
151                    tokio::spawn(async move {
152                        if let Err(err) =
153                            network.direct_message(view, message_bytes, recipient).await
154                        {
155                            tracing::warn!("Failed to send message: {:?}", err);
156                        }
157                    });
158                },
159
160                OutboundMessage::Broadcast(message) => {
161                    let view = message.view_number();
162                    // Wrap it in the real message type
163                    let message_inner = Message {
164                        sender: public_key,
165                        kind: message,
166                    };
167
168                    // Serialize it
169                    let message_bytes =
170                        match BincodeSerializer::<StaticVersion<0, 0>>::serialize(&message_inner) {
171                            Ok(message_bytes) => message_bytes,
172                            Err(err) => {
173                                tracing::warn!("Failed to serialize broadcast message: {}", err);
174                                continue;
175                            },
176                        };
177
178                    // Broadcast the message to the global topic
179                    if let Err(err) = network
180                        .broadcast_message(view, message_bytes, Topic::Global, BroadcastDelay::None)
181                        .await
182                    {
183                        tracing::error!("Failed to broadcast message: {:?}", err);
184                    };
185                },
186            }
187        }
188    }
189
190    /// Send an outbound message through the coordinator's network.
191    ///
192    /// The coordinator's network sends external payloads over the wire
193    /// verbatim, so they must be self-framing: wrap the payload in the same
194    /// versioned `Message` envelope the legacy path uses, which the receiving
195    /// side's fallback decoder recognizes and unwraps.
196    async fn send_via_coordinator(
197        client_api: &ClientApi<SeqTypes>,
198        message: OutboundMessage,
199        public_key: PubKey,
200    ) {
201        match message {
202            OutboundMessage::Direct(kind @ MessageKind::External(_), recipient) => {
203                let message = Message {
204                    sender: public_key,
205                    kind,
206                };
207                let message_bytes =
208                    match BincodeSerializer::<StaticVersion<0, 0>>::serialize(&message) {
209                        Ok(message_bytes) => message_bytes,
210                        Err(err) => {
211                            tracing::warn!("Failed to serialize direct message: {}", err);
212                            return;
213                        },
214                    };
215                if let Err(err) = client_api
216                    .send_external_message(message_bytes, recipient)
217                    .await
218                {
219                    tracing::warn!(%err, "failed to send external message via coordinator");
220                }
221            },
222            // All request-response traffic uses batched direct messages; the
223            // coordinator's network has no broadcast topic for external
224            // messages.
225            other => {
226                tracing::warn!(
227                    message = ?other,
228                    "dropping unsupported external message after cutover"
229                );
230            },
231        }
232    }
233}