Skip to main content

request_response/
lib.rs

1//! This crate contains a general request-response protocol. It is used to send requests to
2//! a set of recipients and wait for responses.
3
4use std::{
5    collections::HashMap,
6    future::Future,
7    marker::PhantomData,
8    sync::{
9        Arc,
10        atomic::{AtomicU64, Ordering},
11    },
12    time::{Duration, Instant},
13};
14
15use anyhow::{Context, Result, anyhow};
16use data_source::DataSource;
17use derive_more::derive::Deref;
18use hotshot_types::traits::signature_key::SignatureKey;
19use message::{Message, MessageFrame, RequestMessage, ResponseMessage};
20use network::{Bytes, Receiver, Sender};
21use parking_lot::RwLock;
22use rand::seq::SliceRandom;
23use recipient_source::RecipientSource;
24use request::Request;
25use tokio::{
26    spawn,
27    sync::mpsc,
28    time::{sleep, timeout},
29};
30use tokio_util::task::AbortOnDropHandle;
31use tracing::{debug, error, info, trace, warn};
32use util::{BoundedVecDeque, NamedSemaphore, NamedSemaphoreError};
33
34/// The data source trait. Is what we use to derive the response data for a request
35pub mod data_source;
36/// The message type. Is the base type for all messages in the request-response protocol
37pub mod message;
38/// The network traits. Is what we use to send and receive messages over the network as
39/// the protocol
40pub mod network;
41/// The recipient source trait. Is what we use to get the recipients that a specific message should
42/// expect responses from
43pub mod recipient_source;
44/// The request trait. Is what we use to define a request and a corresponding response type
45pub mod request;
46/// Utility types and functions
47mod util;
48
49/// A type alias for the hash of a request
50pub type RequestHash = blake3::Hash;
51
52/// The map of active outgoing requests: request hash → the waiters registered by concurrent
53/// [`RequestResponseInner::request`] calls for the same data. Guarded by a synchronous lock so
54/// waiters can deregister themselves in `Drop`; it is never held across an `await`
55type ActiveRequestsMap = Arc<RwLock<HashMap<RequestHash, Vec<Waiter>>>>;
56
57/// A type alias for the list of tasks that are responding to requests
58pub type IncomingRequests<K> = NamedSemaphore<K>;
59
60/// The number of serialized responses each waiter buffers before drops occur. Only one valid
61/// response is needed and batched senders keep re-requesting, so extra responses are free to
62/// drop. Kept small because bodies (leaves, merkle trees, VID shares) can be large and every
63/// in-flight request pins its own buffer
64const RESPONSE_BUFFER_SIZE: usize = 16;
65
66/// The errors that can occur when making a request for data
67#[derive(thiserror::Error, Debug)]
68pub enum RequestError {
69    /// The request timed out
70    #[error("request timed out")]
71    Timeout,
72    /// The request was invalid
73    #[error("request was invalid")]
74    InvalidRequest(anyhow::Error),
75    /// Other errors
76    #[error("other error")]
77    Other(anyhow::Error),
78}
79
80/// A trait for serializing and deserializing a type to and from a byte array. [`Request`] types and
81/// [`Response`] types will need to implement this trait
82pub trait Serializable: Sized {
83    /// Serialize the type to a byte array. If this is for a [`Request`] and your [`Request`] type
84    /// is represented as an enum, please make sure that you serialize it with a unique type ID. Otherwise,
85    /// you may end up with collisions as the request hash is used as a unique identifier
86    ///
87    /// # Errors
88    /// - If the type cannot be serialized to a byte array
89    fn to_bytes(&self) -> Result<Vec<u8>>;
90
91    /// Deserialize the type from a byte array
92    ///
93    /// # Errors
94    /// - If the byte array is not a valid representation of the type
95    fn from_bytes(bytes: &[u8]) -> Result<Self>;
96}
97
98/// The underlying configuration for the request-response protocol
99#[derive(Clone)]
100pub struct RequestResponseConfig {
101    /// The timeout for incoming requests. Do not respond to a request after this threshold
102    /// has passed.
103    pub incoming_request_ttl: Duration,
104    /// The maximum amount of time we will spend trying to both derive a response for a request and
105    /// send the response over the wire.
106    pub incoming_request_timeout: Duration,
107    /// The batch size for outgoing requests. This is the number of request messages that we will
108    /// send out at a time for a single request before waiting for the [`request_batch_interval`].
109    pub request_batch_size: usize,
110    /// The time to wait (per request) between sending out batches of request messages
111    pub request_batch_interval: Duration,
112    /// The maximum (global) number of incoming requests that can be processed at any given time.
113    pub max_incoming_requests: usize,
114    /// The maximum number of incoming requests that can be processed for a single key at any given time.
115    pub max_incoming_requests_per_key: usize,
116}
117
118/// A protocol that allows for request-response communication. Is cheaply cloneable, so there is no
119/// need to wrap it in an `Arc`
120#[derive(Deref)]
121pub struct RequestResponse<
122    S: Sender<K>,
123    R: Receiver,
124    Req: Request,
125    RS: RecipientSource<Req, K>,
126    DS: DataSource<Req>,
127    K: SignatureKey + 'static,
128> {
129    #[deref]
130    /// The inner implementation of the request-response protocol
131    pub inner: Arc<RequestResponseInner<S, R, Req, RS, DS, K>>,
132    /// A handle to the receiving task. This will automatically get cancelled when the protocol is dropped
133    _receiving_task_handle: Arc<AbortOnDropHandle<()>>,
134}
135
136/// We need to manually implement the `Clone` trait for this type because deriving
137/// `Deref` will cause an issue where it tries to clone the inner field instead
138impl<
139    S: Sender<K>,
140    R: Receiver,
141    Req: Request,
142    RS: RecipientSource<Req, K>,
143    DS: DataSource<Req>,
144    K: SignatureKey + 'static,
145> Clone for RequestResponse<S, R, Req, RS, DS, K>
146{
147    fn clone(&self) -> Self {
148        Self {
149            inner: Arc::clone(&self.inner),
150            _receiving_task_handle: Arc::clone(&self._receiving_task_handle),
151        }
152    }
153}
154
155impl<
156    S: Sender<K>,
157    R: Receiver,
158    Req: Request,
159    RS: RecipientSource<Req, K>,
160    DS: DataSource<Req>,
161    K: SignatureKey + 'static,
162> RequestResponse<S, R, Req, RS, DS, K>
163{
164    /// Create a new [`RequestResponseProtocol`]
165    pub fn new(
166        // The configuration for the protocol
167        config: RequestResponseConfig,
168        // The network sender that [`RequestResponseProtocol`] will use to send messages
169        sender: S,
170        // The network receiver that [`RequestResponseProtocol`] will use to receive messages
171        receiver: R,
172        // The recipient source that [`RequestResponseProtocol`] will use to get the recipients
173        // that a specific message should expect responses from
174        recipient_source: RS,
175        // The [response] data source that [`RequestResponseProtocol`] will use to derive the
176        // response data for a specific request
177        data_source: DS,
178    ) -> Self {
179        // Create the inner implementation
180        let inner = Arc::new(RequestResponseInner {
181            config,
182            sender,
183            recipient_source,
184            data_source,
185            active_requests: ActiveRequestsMap::default(),
186            next_waiter_id: AtomicU64::new(0),
187            phantom_data: PhantomData,
188        });
189
190        // Start the task that receives messages and handles them. This will automatically get cancelled
191        // when the protocol is dropped
192        let inner_clone = Arc::clone(&inner);
193        let receive_task_handle =
194            AbortOnDropHandle::new(tokio::spawn(inner_clone.receiving_task(receiver)));
195
196        // Return the protocol
197        Self {
198            inner,
199            _receiving_task_handle: Arc::new(receive_task_handle),
200        }
201    }
202}
203
204/// The inner implementation for the request-response protocol
205pub struct RequestResponseInner<
206    S: Sender<K>,
207    R: Receiver,
208    Req: Request,
209    RS: RecipientSource<Req, K>,
210    DS: DataSource<Req>,
211    K: SignatureKey + 'static,
212> {
213    /// The configuration of the protocol
214    config: RequestResponseConfig,
215    /// The sender to use for the protocol
216    pub sender: S,
217    /// The recipient source to use for the protocol
218    pub recipient_source: RS,
219    /// The data source to use for the protocol
220    data_source: DS,
221    /// The map of currently active, outgoing requests
222    active_requests: ActiveRequestsMap,
223    /// The id to assign to the next registered waiter
224    next_waiter_id: AtomicU64,
225    /// Phantom data to help with type inference
226    phantom_data: PhantomData<(K, R, Req, DS)>,
227}
228impl<
229    S: Sender<K>,
230    R: Receiver,
231    Req: Request,
232    RS: RecipientSource<Req, K>,
233    DS: DataSource<Req>,
234    K: SignatureKey + 'static,
235> RequestResponseInner<S, R, Req, RS, DS, K>
236{
237    /// Request something from the protocol indefinitely until we get a response
238    /// or there was a critical error (e.g. the request could not be signed)
239    ///
240    /// # Errors
241    /// - If the request was invalid
242    /// - If there was a critical error (e.g. the channel was closed)
243    pub async fn request_indefinitely<F, Fut, O>(
244        self: &Arc<Self>,
245        public_key: &K,
246        private_key: &K::PrivateKey,
247        // The estimated TTL of other participants. This is used to decide when to
248        // stop making requests and sign a new one
249        estimated_request_ttl: Duration,
250        // The request to make
251        request: Req,
252        // The response validation function
253        response_validation_fn: F,
254    ) -> std::result::Result<O, RequestError>
255    where
256        F: Fn(&Req, Req::Response) -> Fut + Send + Sync + Clone,
257        Fut: Future<Output = anyhow::Result<O>> + Send,
258        O: Send,
259    {
260        loop {
261            // Sign a request message
262            let request_message = RequestMessage::new_signed(public_key, private_key, &request)
263                .map_err(|e| {
264                    RequestError::InvalidRequest(anyhow::anyhow!(
265                        "failed to sign request message: {e}"
266                    ))
267                })?;
268
269            // Request the data, handling the errors appropriately
270            match self
271                .request(
272                    request_message,
273                    estimated_request_ttl,
274                    response_validation_fn.clone(),
275                )
276                .await
277            {
278                Ok(response) => return Ok(response),
279                Err(RequestError::Timeout) => continue,
280                Err(e) => return Err(e),
281            }
282        }
283    }
284
285    /// Request something from the protocol and wait for the first response that passes
286    /// validation. Concurrent requests for the same data (determined by `Blake3` hash of the
287    /// request) share incoming responses, but each caller validates them independently and all
288    /// of them make requests until their own timeout is reached.
289    ///
290    /// # Errors
291    /// - If the request times out
292    /// - If the request we sign is invalid
293    pub async fn request<F, Fut, O>(
294        self: &Arc<Self>,
295        request_message: RequestMessage<Req, K>,
296        timeout_duration: Duration,
297        response_validation_fn: F,
298    ) -> std::result::Result<O, RequestError>
299    where
300        F: Fn(&Req, Req::Response) -> Fut + Send + Sync,
301        Fut: Future<Output = anyhow::Result<O>> + Send,
302        O: Send,
303    {
304        // The hash identifies the request on the wire and joins concurrent callers
305        let request_hash = blake3::hash(&request_message.request.to_bytes().map_err(|e| {
306            RequestError::InvalidRequest(anyhow::anyhow!(
307                "failed to serialize request message: {e}"
308            ))
309        })?);
310
311        // Register before sending so no response can be missed. Deregisters itself when
312        // dropped, on success, timeout, and cancellation alike
313        let mut response_receiver = self.register_waiter(request_hash);
314
315        let message = Bytes::from(
316            Message::Request(request_message.clone())
317                .to_bytes()
318                .map_err(|e| {
319                    RequestError::InvalidRequest(anyhow::anyhow!(
320                        "failed to serialize request message: {e}"
321                    ))
322                })?,
323        );
324
325        // The deadline covers sending too: looking up recipients and sending can both park
326        timeout(timeout_duration, async {
327            // The request is re-sent by a background task that is aborted when the handle drops
328            // with this function
329            let _batched_sending_task = self
330                .spawn_batched_sender(request_message.clone(), message, timeout_duration)
331                .await?;
332
333            loop {
334                let body = response_receiver.recv().await.ok_or_else(|| {
335                    // Unreachable: the active-requests map holds a sender for as long as
336                    // `response_receiver` is registered, and it deregisters only on drop
337                    RequestError::Other(anyhow!("response channel closed"))
338                })?;
339
340                // Decoding here rather than in the receiving task keeps that task a pure
341                // router: a flood of responses costs only the callers that asked for them,
342                // bounded by their buffers
343                let response = match Req::Response::from_bytes(&body) {
344                    Ok(response) => response,
345                    Err(e) => {
346                        debug!("Received undecodable response: {e:#}");
347                        continue;
348                    },
349                };
350
351                match response_validation_fn(&request_message.request, response).await {
352                    Ok(validated) => return Ok(validated),
353                    Err(e) => debug!("Received invalid response: {e:#}"),
354                }
355            }
356        })
357        .await
358        .map_err(|_| RequestError::Timeout)?
359    }
360
361    /// Register a waiter for responses to the request with the given hash
362    fn register_waiter(self: &Arc<Self>, request_hash: RequestHash) -> ResponseReceiver {
363        let (sender, receiver) = mpsc::channel(RESPONSE_BUFFER_SIZE);
364        let id = self.next_waiter_id.fetch_add(1, Ordering::Relaxed);
365
366        self.active_requests
367            .write()
368            .entry(request_hash)
369            .or_default()
370            .push(Waiter { id, sender });
371
372        ResponseReceiver {
373            request_hash,
374            id,
375            receiver,
376            active_requests: Arc::clone(&self.active_requests),
377        }
378    }
379
380    /// Spawn the task that repeatedly sends a batched request to
381    /// [`config.request_batch_size`] recipients at a time until `timeout_duration` elapses
382    /// or the returned handle is dropped
383    async fn spawn_batched_sender(
384        self: &Arc<Self>,
385        request_message: RequestMessage<Req, K>,
386        message: Bytes,
387        timeout_duration: Duration,
388    ) -> std::result::Result<AbortOnDropHandle<()>, RequestError> {
389        // Shuffle so we don't always send to the same recipients in the same order
390        let mut recipients = self
391            .recipient_source
392            .get_expected_responders(&request_message.request)
393            .await
394            .map_err(|e| {
395                RequestError::InvalidRequest(anyhow::anyhow!(
396                    "failed to get expected responders for request: {e}"
397                ))
398            })?;
399        recipients.shuffle(&mut rand::thread_rng());
400
401        let start_time = Instant::now();
402
403        let self_clone = Arc::clone(self);
404        Ok(AbortOnDropHandle::new(spawn(async move {
405            // Without this the loop below would have no await point and spin until the deadline.
406            // A single-validator network hits it, as the expected responders exclude ourselves
407            if recipients.is_empty() {
408                debug!("No recipients for request {request_message:?}");
409                return;
410            }
411
412            // At most `request_batch_size` sends in flight at a time: pushing beyond the queue's
413            // capacity evicts (and thereby aborts) the oldest send task
414            let mut outgoing_requests = BoundedVecDeque::new(self_clone.config.request_batch_size);
415
416            while start_time.elapsed() < timeout_duration {
417                for recipient_batch in recipients.chunks(self_clone.config.request_batch_size) {
418                    for recipient in recipient_batch {
419                        let self_clone = Arc::clone(&self_clone);
420                        let request_message_clone = request_message.clone();
421                        let recipient_clone = recipient.clone();
422                        let message_clone = Arc::clone(&message);
423
424                        let individual_sending_task = spawn(async move {
425                            trace!(
426                                "Sending request {request_message_clone:?} to {recipient_clone:?}"
427                            );
428
429                            let _ = self_clone
430                                .sender
431                                .send_direct_message(&message_clone, recipient_clone)
432                                .await;
433                        });
434
435                        outgoing_requests.push(AbortOnDropHandle::new(individual_sending_task));
436                    }
437
438                    sleep(self_clone.config.request_batch_interval).await;
439                }
440            }
441        })))
442    }
443
444    /// The task responsible for receiving messages from the receiver and handling them
445    async fn receiving_task(self: Arc<Self>, mut receiver: R) {
446        // Upper bound the number of concurrently processed incoming requests
447        let mut incoming_requests = NamedSemaphore::new(
448            self.config.max_incoming_requests_per_key,
449            Some(self.config.max_incoming_requests),
450        );
451
452        loop {
453            match receiver.receive_message().await {
454                Ok(message) => {
455                    if let Err(e) = self.handle_message(&message, &mut incoming_requests) {
456                        warn!("Received invalid message: {e:#}");
457                    }
458                },
459                // An error here means the receiver will _NEVER_ receive any more messages
460                Err(e) => {
461                    error!("Request/response receive task exited: {e:#}");
462                    return;
463                },
464            }
465        }
466    }
467
468    /// Route a message by its framing, deserializing only what will be acted on
469    fn handle_message(
470        self: &Arc<Self>,
471        message: &[u8],
472        incoming_requests: &mut IncomingRequests<K>,
473    ) -> Result<()> {
474        match MessageFrame::parse(message)? {
475            MessageFrame::Request { body } => {
476                self.handle_request(RequestMessage::from_bytes(body)?, incoming_requests);
477            },
478            MessageFrame::Response { request_hash, body } => {
479                self.handle_response(request_hash, body);
480            },
481        }
482        Ok(())
483    }
484
485    /// Handle a request sent to us
486    fn handle_request(
487        self: &Arc<Self>,
488        request_message: RequestMessage<Req, K>,
489        incoming_requests: &mut IncomingRequests<K>,
490    ) {
491        trace!("Handling request {:?}", request_message);
492
493        // Spawn a task to:
494        // - Validate the request
495        // - Derive the response data (check if we have it)
496        // - Send the response to the requester
497        let self_clone = Arc::clone(self);
498
499        // Attempt to acquire a permit for the request. Warn if there are too many requests currently being processed
500        // either globally or per-key
501        let permit = incoming_requests.try_acquire(request_message.public_key.clone());
502        match permit {
503            Ok(ref permit) => permit,
504            Err(NamedSemaphoreError::PerKeyLimitReached) => {
505                info!(
506                    "Failed to process request from {}: too many requests from the same key are \
507                     already being processed",
508                    request_message.public_key
509                );
510                return;
511            },
512            Err(NamedSemaphoreError::GlobalLimitReached) => {
513                info!(
514                    "Failed to process request from {}: too many requests are already being \
515                     processed",
516                    request_message.public_key
517                );
518                return;
519            },
520        };
521
522        tokio::spawn(async move {
523            let result = timeout(self_clone.config.incoming_request_timeout, async move {
524                // Validate the request message. This includes:
525                // - Checking the signature and making sure it's valid
526                // - Checking the timestamp and making sure it's not too old
527                // - Calling the request's application-specific validation function
528                request_message
529                    .validate(self_clone.config.incoming_request_ttl)
530                    .with_context(|| "failed to validate request")?;
531
532                // Try to fetch the response data from the data source
533                let response = self_clone
534                    .data_source
535                    .derive_response_for(&request_message.request)
536                    .await
537                    .with_context(|| "failed to derive response for request")?;
538
539                // Create the response message and serialize it
540                let response = Bytes::from(
541                    Message::Response::<Req, K>(ResponseMessage {
542                        request_hash: blake3::hash(&request_message.request.to_bytes()?),
543                        response,
544                    })
545                    .to_bytes()
546                    .with_context(|| "failed to serialize response message")?,
547                );
548
549                // Send the response to the requester
550                self_clone
551                    .sender
552                    .send_direct_message(&response, request_message.public_key)
553                    .await
554                    .with_context(|| "failed to send response to requester")?;
555
556                // Drop the permit
557                _ = permit;
558                drop(permit);
559
560                Ok::<(), anyhow::Error>(())
561            })
562            .await
563            .map_err(|_| anyhow::anyhow!("timed out while sending response"))
564            .and_then(|result| result);
565
566            if let Err(e) = result {
567                debug!("Failed to send response to requester: {e:#}");
568            }
569        });
570    }
571
572    /// Handle a response sent to us: fan its serialized body out to every waiter registered for
573    /// the request hash. Each waiter decodes the body itself, so a response nobody is waiting
574    /// for costs no more than this lookup
575    fn handle_response(&self, request_hash: RequestHash, body: &[u8]) {
576        trace!("Handling response for request {request_hash}");
577
578        // Snapshot the waiting senders so the lock is not held while sending
579        let waiters: Vec<mpsc::Sender<Bytes>> = {
580            let active_requests = self.active_requests.read();
581            let Some(waiters) = active_requests.get(&request_hash) else {
582                // Not an error: a response for a request that was already satisfied or timed out
583                trace!("Received response for inactive request {request_hash}");
584                return;
585            };
586            waiters.iter().map(|waiter| waiter.sender.clone()).collect()
587        };
588
589        // `try_send` drops the response when a waiter's buffer is full: that waiter is
590        // backlogged with earlier candidates, and batched senders keep re-requesting until
591        // satisfied. A waiter dropped concurrently just yields a `Closed` error, also ignored
592        let body = Bytes::from(body.to_vec());
593        for waiter in waiters {
594            if let Err(mpsc::error::TrySendError::Full(_)) = waiter.try_send(Arc::clone(&body)) {
595                debug!("Dropped response for request {request_hash}: waiter buffer full");
596            }
597        }
598    }
599}
600
601/// A waiter registered by one [`RequestResponseInner::request`] call, to which incoming
602/// responses for its request hash are delivered
603struct Waiter {
604    /// Identifies this waiter for deregistration when its receiver is dropped
605    id: u64,
606    /// Delivers the serialized bodies of candidate responses to the corresponding
607    /// [`ResponseReceiver`]
608    sender: mpsc::Sender<Bytes>,
609}
610
611/// Receives candidate responses for one [`RequestResponseInner::request`] call. Deregisters
612/// the waiter when dropped, so map entries are cleaned up on success, timeout, and
613/// cancellation alike
614struct ResponseReceiver {
615    /// The request hash this receiver's waiter is registered under
616    request_hash: RequestHash,
617    /// The id of this receiver's [`Waiter`]
618    id: u64,
619    /// Receives the serialized bodies of candidate responses
620    receiver: mpsc::Receiver<Bytes>,
621    /// The map to deregister from on drop
622    active_requests: ActiveRequestsMap,
623}
624
625impl ResponseReceiver {
626    async fn recv(&mut self) -> Option<Bytes> {
627        self.receiver.recv().await
628    }
629}
630
631impl Drop for ResponseReceiver {
632    fn drop(&mut self) {
633        let mut active_requests = self.active_requests.write();
634        if let Some(waiters) = active_requests.get_mut(&self.request_hash) {
635            waiters.retain(|waiter| waiter.id != self.id);
636            if waiters.is_empty() {
637                active_requests.remove(&self.request_hash);
638            }
639        }
640    }
641}
642
643#[cfg(test)]
644mod tests {
645    use std::{
646        collections::HashMap,
647        sync::{
648            Mutex,
649            atomic::{AtomicBool, AtomicUsize},
650        },
651    };
652
653    use async_trait::async_trait;
654    use hotshot_types::signature_key::{BLSPrivKey, BLSPubKey};
655    use rand::Rng;
656    use tokio::{sync::mpsc, task::JoinSet};
657
658    use super::*;
659
660    /// A test sender that has a list of all the participants in the network
661    #[derive(Clone)]
662    pub struct TestSender {
663        network: Arc<HashMap<BLSPubKey, mpsc::Sender<Bytes>>>,
664    }
665
666    /// An implementation of the [`Sender`] trait for the [`TestSender`] type
667    #[async_trait]
668    impl Sender<BLSPubKey> for TestSender {
669        async fn send_direct_message(&self, message: &Bytes, recipient: BLSPubKey) -> Result<()> {
670            self.network
671                .get(&recipient)
672                .ok_or(anyhow::anyhow!("recipient not found"))?
673                .send(Arc::clone(message))
674                .await
675                .map_err(|_| anyhow::anyhow!("failed to send message"))?;
676
677            Ok(())
678        }
679    }
680
681    // Implement the [`RecipientSource`] trait for the [`TestSender`] type
682    #[async_trait]
683    impl RecipientSource<TestRequest, BLSPubKey> for TestSender {
684        async fn get_expected_responders(&self, _request: &TestRequest) -> Result<Vec<BLSPubKey>> {
685            // Get all the participants in the network
686            Ok(self.network.keys().copied().collect())
687        }
688    }
689
690    // Create a test request that is just some bytes
691    #[derive(Clone, Debug)]
692    struct TestRequest(Vec<u8>);
693
694    // Implement the [`Serializable`] trait for the [`TestRequest`] type
695    impl Serializable for TestRequest {
696        fn to_bytes(&self) -> Result<Vec<u8>> {
697            Ok(self.0.clone())
698        }
699
700        fn from_bytes(bytes: &[u8]) -> Result<Self> {
701            Ok(TestRequest(bytes.to_vec()))
702        }
703    }
704
705    // Implement the [`Request`] trait for the [`TestRequest`] type
706    impl Request for TestRequest {
707        type Response = Vec<u8>;
708        fn validate(&self) -> Result<()> {
709            Ok(())
710        }
711    }
712
713    // Create a test data source that pretends to have the data or not
714    #[derive(Clone)]
715    struct TestDataSource {
716        /// Whether we have the data or not
717        has_data: bool,
718        /// The time at which the data will be available if we have it
719        data_available_time: Instant,
720
721        /// Whether or not the data will be taken once served
722        take_data: bool,
723        /// Whether or not the data has been taken
724        taken: Arc<AtomicBool>,
725    }
726
727    #[async_trait]
728    impl DataSource<TestRequest> for TestDataSource {
729        async fn derive_response_for(&self, request: &TestRequest) -> Result<Vec<u8>> {
730            // Return a response if we hit the hit rate
731            if self.has_data && Instant::now() >= self.data_available_time {
732                if self.take_data && !self.taken.swap(true, std::sync::atomic::Ordering::Relaxed) {
733                    return Err(anyhow::anyhow!("data already taken"));
734                }
735                Ok(blake3::hash(&request.0).as_bytes().to_vec())
736            } else {
737                Err(anyhow::anyhow!("did not have the data"))
738            }
739        }
740    }
741
742    /// Create and return a default protocol configuration
743    fn default_protocol_config() -> RequestResponseConfig {
744        RequestResponseConfig {
745            incoming_request_ttl: Duration::from_secs(40),
746            incoming_request_timeout: Duration::from_secs(40),
747            request_batch_size: 10,
748            request_batch_interval: Duration::from_millis(100),
749            max_incoming_requests: 10,
750            max_incoming_requests_per_key: 1,
751        }
752    }
753
754    /// Create fully connected test networks with `num_participants` participants
755    fn create_participants(
756        num: usize,
757    ) -> Vec<(TestSender, mpsc::Receiver<Bytes>, (BLSPubKey, BLSPrivKey))> {
758        // The entire network
759        let mut network = HashMap::new();
760
761        // All receivers in the network
762        let mut receivers = Vec::new();
763
764        // All keypairs in the network
765        let mut keypairs = Vec::new();
766
767        // For each participant,
768        for i in 0..num {
769            // Create a unique `BLSPubKey`
770            let (public_key, private_key) =
771                BLSPubKey::generated_from_seed_indexed([2; 32], i.try_into().unwrap());
772
773            // Add the keypair to the list
774            keypairs.push((public_key, private_key));
775
776            // Create a channel for sending and receiving messages
777            let (sender, receiver) = mpsc::channel::<Bytes>(100);
778
779            // Add the participant to the network
780            network.insert(public_key, sender);
781
782            // Add the receiver to the list of receivers
783            receivers.push(receiver);
784        }
785
786        // Create a test sender from the network
787        let sender = TestSender {
788            network: Arc::new(network),
789        };
790
791        // Return all senders and receivers
792        receivers
793            .into_iter()
794            .zip(keypairs)
795            .map(|(r, k)| (sender.clone(), r, k))
796            .collect()
797    }
798
799    /// The configuration for an integration test
800    #[derive(Clone)]
801    struct IntegrationTestConfig {
802        /// The request response protocol configuration
803        request_response_config: RequestResponseConfig,
804        /// The number of participants in the network
805        num_participants: usize,
806        /// The number of participants that have the data
807        num_participants_with_data: usize,
808        /// The timeout for the requests
809        request_timeout: Duration,
810        /// The delay before the nodes have the data available
811        data_available_delay: Duration,
812    }
813
814    /// The result of an integration test
815    struct IntegrationTestResult {
816        /// The number of nodes that received a response
817        num_succeeded: usize,
818    }
819
820    /// Run an integration test with the given parameters
821    async fn run_integration_test(config: IntegrationTestConfig) -> IntegrationTestResult {
822        // Create a fully connected network with `num_participants` participants
823        let participants = create_participants(config.num_participants);
824
825        // Create a join set to wait for all the tasks to finish
826        let mut join_set = JoinSet::new();
827
828        // We need to keep these here so they don't get dropped
829        let handles = Arc::new(Mutex::new(Vec::new()));
830
831        // For each one, create a new [`RequestResponse`] protocol
832        for (i, (sender, receiver, (public_key, private_key))) in
833            participants.into_iter().enumerate()
834        {
835            let config_clone = config.request_response_config.clone();
836            let handles_clone = Arc::clone(&handles);
837            join_set.spawn(async move {
838                let protocol = RequestResponse::new(
839                    config_clone,
840                    sender.clone(),
841                    receiver,
842                    sender,
843                    TestDataSource {
844                        has_data: i < config.num_participants_with_data,
845                        data_available_time: Instant::now() + config.data_available_delay,
846                        take_data: false,
847                        taken: Arc::new(AtomicBool::new(false)),
848                    },
849                );
850
851                // Add the handle to the handles list so it doesn't get dropped and
852                // cancelled
853                #[allow(clippy::used_underscore_binding)]
854                handles_clone
855                    .lock()
856                    .unwrap()
857                    .push(Arc::clone(&protocol._receiving_task_handle));
858
859                // Create a random request
860                let request = TestRequest(vec![rand::thread_rng().r#gen(); 100]);
861
862                // Get the hash of the request
863                let request_hash = blake3::hash(&request.0).as_bytes().to_vec();
864
865                // Create a new request message
866                let request = RequestMessage::new_signed(&public_key, &private_key, &request)
867                    .expect("failed to create request message");
868
869                // Request the data from the protocol
870                let response = protocol
871                    .request(
872                        request,
873                        config.request_timeout,
874                        |_request, response| async move { Ok(response) },
875                    )
876                    .await?;
877
878                // Make sure the response is the hash of the request
879                assert_eq!(response, request_hash);
880
881                Ok::<(), anyhow::Error>(())
882            });
883        }
884
885        // Wait for all the tasks to finish
886        let mut num_succeeded = config.num_participants;
887        while let Some(result) = join_set.join_next().await {
888            if result.is_err() || result.unwrap().is_err() {
889                num_succeeded -= 1;
890            }
891        }
892
893        IntegrationTestResult { num_succeeded }
894    }
895
896    /// Test the integration of the protocol with 50% of the participants having the data
897    #[tokio::test(flavor = "multi_thread")]
898    async fn test_integration_50_0s() {
899        // Build a config
900        let config = IntegrationTestConfig {
901            request_response_config: default_protocol_config(),
902            num_participants: 100,
903            num_participants_with_data: 50,
904            request_timeout: Duration::from_secs(40),
905            data_available_delay: Duration::from_secs(0),
906        };
907
908        // Run the test, making sure all the requests succeed
909        let result = run_integration_test(config).await;
910        assert_eq!(result.num_succeeded, 100);
911    }
912
913    /// Test the integration of the protocol when nobody has the data. Make sure we don't
914    /// get any responses
915    #[tokio::test(flavor = "multi_thread")]
916    async fn test_integration_0() {
917        // Build a config
918        let config = IntegrationTestConfig {
919            request_response_config: default_protocol_config(),
920            num_participants: 100,
921            num_participants_with_data: 0,
922            request_timeout: Duration::from_secs(40),
923            data_available_delay: Duration::from_secs(0),
924        };
925
926        // Run the test
927        let result = run_integration_test(config).await;
928
929        // Make sure all the requests succeeded
930        assert_eq!(result.num_succeeded, 0);
931    }
932
933    /// Test the integration of the protocol when one node has the data after
934    /// a delay of 1s
935    #[tokio::test(flavor = "multi_thread")]
936    async fn test_integration_1_1s() {
937        // Build a config
938        let config = IntegrationTestConfig {
939            request_response_config: default_protocol_config(),
940            num_participants: 100,
941            num_participants_with_data: 1,
942            request_timeout: Duration::from_secs(40),
943            data_available_delay: Duration::from_secs(2),
944        };
945
946        // Run the test
947        let result = run_integration_test(config).await;
948
949        // Make sure all the requests succeeded
950        assert_eq!(result.num_succeeded, 100);
951    }
952
953    /// Test that we can join an existing request for the same data and get the same (single) response
954    #[tokio::test(flavor = "multi_thread")]
955    async fn test_join_existing_request() {
956        // Build a config
957        let config = default_protocol_config();
958
959        // Create two participants
960        let mut participants = Vec::new();
961
962        for (sender, receiver, (public_key, private_key)) in create_participants(2) {
963            // For each, create a new [`RequestResponse`] protocol
964            let protocol = RequestResponse::new(
965                config.clone(),
966                sender.clone(),
967                receiver,
968                sender,
969                TestDataSource {
970                    take_data: true,
971                    has_data: true,
972                    data_available_time: Instant::now() + Duration::from_secs(2),
973                    taken: Arc::new(AtomicBool::new(false)),
974                },
975            );
976
977            // Add the participants to the list
978            participants.push((protocol, public_key, private_key));
979        }
980
981        // Take the first participant
982        let one = Arc::new(participants.remove(0));
983
984        // Create the request that they should all be able to join on
985        let request = TestRequest(vec![rand::thread_rng().r#gen(); 100]);
986
987        // Create a join set to wait for all the tasks to finish
988        let mut join_set = JoinSet::new();
989
990        // Make 10 requests with the same hash
991        for _ in 0..10 {
992            // Clone the first participant
993            let one_clone = Arc::clone(&one);
994
995            // Clone the request
996            let request_clone = request.clone();
997
998            // Spawn a task to request the data
999            join_set.spawn(async move {
1000                // Create a new, signed request message
1001                let request_message =
1002                    RequestMessage::new_signed(&one_clone.1, &one_clone.2, &request_clone)?;
1003
1004                // Start requesting it
1005                one_clone
1006                    .0
1007                    .request(
1008                        request_message,
1009                        Duration::from_secs(20),
1010                        |_request, response| async move { Ok(response) },
1011                    )
1012                    .await?;
1013
1014                Ok::<(), anyhow::Error>(())
1015            });
1016        }
1017
1018        // Wait for all the tasks to finish, making sure they all succeed
1019        while let Some(result) = join_set.join_next().await {
1020            result
1021                .expect("failed to join task")
1022                .expect("failed to request data");
1023        }
1024    }
1025
1026    /// The concrete protocol type used in tests
1027    type TestProtocol = RequestResponse<
1028        TestSender,
1029        mpsc::Receiver<Bytes>,
1030        TestRequest,
1031        TestSender,
1032        TestDataSource,
1033        BLSPubKey,
1034    >;
1035
1036    /// Create a protocol per participant, where each participant may or may not have the data
1037    fn create_protocols(
1038        num: usize,
1039        has_data: bool,
1040    ) -> Vec<(TestProtocol, (BLSPubKey, BLSPrivKey))> {
1041        create_participants(num)
1042            .into_iter()
1043            .map(|(sender, receiver, keypair)| {
1044                let protocol = RequestResponse::new(
1045                    default_protocol_config(),
1046                    sender.clone(),
1047                    receiver,
1048                    sender,
1049                    TestDataSource {
1050                        has_data,
1051                        data_available_time: Instant::now(),
1052                        take_data: false,
1053                        taken: Arc::new(AtomicBool::new(false)),
1054                    },
1055                );
1056                (protocol, keypair)
1057            })
1058            .collect()
1059    }
1060
1061    /// A recipient source that never returns, standing in for one stuck behind consensus
1062    struct StalledRecipientSource;
1063
1064    #[async_trait]
1065    impl RecipientSource<TestRequest, BLSPubKey> for StalledRecipientSource {
1066        async fn get_expected_responders(&self, _request: &TestRequest) -> Result<Vec<BLSPubKey>> {
1067            std::future::pending().await
1068        }
1069    }
1070
1071    /// Test that the request deadline also covers sending, so a stalled send still times out
1072    #[tokio::test(flavor = "multi_thread")]
1073    async fn test_timeout_covers_sending() {
1074        let (sender, receiver, (public_key, private_key)) = create_participants(1).remove(0);
1075        let protocol = RequestResponse::new(
1076            default_protocol_config(),
1077            sender,
1078            receiver,
1079            StalledRecipientSource,
1080            TestDataSource {
1081                has_data: false,
1082                data_available_time: Instant::now(),
1083                take_data: false,
1084                taken: Arc::new(AtomicBool::new(false)),
1085            },
1086        );
1087
1088        let request_message =
1089            RequestMessage::new_signed(&public_key, &private_key, &TestRequest(vec![1, 2, 3]))
1090                .expect("failed to create request message");
1091
1092        let result = timeout(
1093            Duration::from_secs(5),
1094            protocol.request(
1095                request_message,
1096                Duration::from_millis(250),
1097                |_request, response| async move { Ok(response) },
1098            ),
1099        )
1100        .await
1101        .expect("request outlived its own timeout");
1102        assert!(matches!(result, Err(RequestError::Timeout)));
1103    }
1104
1105    /// Test that a timed-out request deregisters its waiter (active-requests map leak regression)
1106    #[tokio::test(flavor = "multi_thread")]
1107    async fn test_waiter_cleanup_on_timeout() {
1108        // Nobody has the data, so the request must time out
1109        let mut protocols = create_protocols(2, false);
1110        let (protocol, (public_key, private_key)) = protocols.remove(0);
1111
1112        let request_message =
1113            RequestMessage::new_signed(&public_key, &private_key, &TestRequest(vec![1, 2, 3]))
1114                .expect("failed to create request message");
1115
1116        let result = protocol
1117            .request(
1118                request_message,
1119                Duration::from_millis(250),
1120                |_request, response| async move { Ok(response) },
1121            )
1122            .await;
1123        assert!(matches!(result, Err(RequestError::Timeout)));
1124
1125        // The waiter must have deregistered itself
1126        assert!(protocol.active_requests.read().is_empty());
1127    }
1128
1129    /// Test that two concurrent requests for the same data can validate to different output
1130    /// types. With the previous type-erased validation this would have failed at runtime with
1131    /// a downcast error
1132    #[tokio::test(flavor = "multi_thread")]
1133    async fn test_join_with_different_output_types() {
1134        let protocols = create_protocols(2, true);
1135        let (requester, (public_key, private_key)) = &protocols[0];
1136
1137        let request = TestRequest(vec![5; 100]);
1138        let expected = blake3::hash(&request.0).as_bytes().to_vec();
1139
1140        let request_message_1 = RequestMessage::new_signed(public_key, private_key, &request)
1141            .expect("failed to create request message");
1142        let request_message_2 = RequestMessage::new_signed(public_key, private_key, &request)
1143            .expect("failed to create request message");
1144
1145        // One caller validates to the raw bytes, the other to their length
1146        let (bytes, length) = tokio::join!(
1147            requester.request(
1148                request_message_1,
1149                Duration::from_secs(20),
1150                |_request, response| async move { Ok(response) },
1151            ),
1152            requester.request(
1153                request_message_2,
1154                Duration::from_secs(20),
1155                |_request, response| async move { Ok(response.len()) },
1156            )
1157        );
1158
1159        assert_eq!(bytes.expect("bytes request failed"), expected);
1160        assert_eq!(length.expect("length request failed"), expected.len());
1161    }
1162
1163    /// Test that an invalid response does not complete a request, but a later valid one does
1164    #[tokio::test(flavor = "multi_thread")]
1165    async fn test_invalid_then_valid_response() {
1166        let protocols = create_protocols(2, true);
1167        let (requester, (public_key, private_key)) = &protocols[0];
1168
1169        // Reject the first response we see and accept any later one
1170        let attempts = Arc::new(AtomicUsize::new(0));
1171        let attempts_clone = Arc::clone(&attempts);
1172        let validation_fn = move |_request: &TestRequest, response: Vec<u8>| {
1173            let attempts = Arc::clone(&attempts_clone);
1174            async move {
1175                if attempts.fetch_add(1, std::sync::atomic::Ordering::SeqCst) == 0 {
1176                    return Err(anyhow::anyhow!("rejecting the first response"));
1177                }
1178                Ok(response)
1179            }
1180        };
1181
1182        let request = TestRequest(vec![7; 100]);
1183        let request_message = RequestMessage::new_signed(public_key, private_key, &request)
1184            .expect("failed to create request message");
1185        let response = requester
1186            .request(request_message, Duration::from_secs(20), validation_fn)
1187            .await
1188            .expect("request failed");
1189
1190        assert_eq!(response, blake3::hash(&request.0).as_bytes().to_vec());
1191        assert!(attempts.load(std::sync::atomic::Ordering::SeqCst) >= 2);
1192
1193        // The waiter must have deregistered itself on success as well
1194        assert!(requester.active_requests.read().is_empty());
1195    }
1196
1197    /// Test that malformed frames and responses to requests nobody made are ignored without
1198    /// disturbing an in-flight request
1199    #[tokio::test(flavor = "multi_thread")]
1200    async fn test_malformed_messages_are_ignored() {
1201        let protocols = create_protocols(2, true);
1202        let (requester, (public_key, private_key)) = &protocols[0];
1203        let inbox = requester
1204            .sender
1205            .network
1206            .get(public_key)
1207            .expect("requester has an inbox");
1208
1209        // Queue junk ahead of any real response: an empty message, an unknown type byte, a
1210        // response too short to carry a hash, and a well-formed response to an unknown request
1211        let mut unknown_response = vec![1];
1212        unknown_response.extend_from_slice(blake3::hash(b"nobody asked").as_bytes());
1213        unknown_response.extend_from_slice(b"payload");
1214        for junk in [vec![], vec![7], vec![1; 20], unknown_response] {
1215            inbox
1216                .send(Bytes::from(junk))
1217                .await
1218                .expect("failed to inject");
1219        }
1220
1221        let request = TestRequest(vec![11; 100]);
1222        let request_message = RequestMessage::new_signed(public_key, private_key, &request)
1223            .expect("failed to create request message");
1224        let response = requester
1225            .request(
1226                request_message,
1227                Duration::from_secs(20),
1228                |_request, response| async move { Ok(response) },
1229            )
1230            .await
1231            .expect("request failed");
1232
1233        assert_eq!(response, blake3::hash(&request.0).as_bytes().to_vec());
1234        assert!(requester.active_requests.read().is_empty());
1235    }
1236}