Skip to main content

commonware_glue/stateful/probe/
mailbox.rs

1use commonware_actor::mailbox::{Policy, Sender};
2use commonware_consensus::{
3    marshal::core::{Mailbox as MarshalMailbox, Variant},
4    simplex::types::Finalization,
5};
6use commonware_cryptography::certificate::Scheme;
7use commonware_utils::channel::oneshot;
8use std::collections::VecDeque;
9
10/// A message that can be sent to the [`Probe`](super::Probe).
11pub(crate) enum Message<S, V>
12where
13    S: Scheme,
14    V: Variant,
15{
16    /// Subscribe for the receipt of the floor finalization from peers.
17    Subscribe {
18        /// The response channel to send the finalization to.
19        response: oneshot::Sender<Finalization<S, V::Commitment>>,
20    },
21    /// Attach a marshal mailbox, moving the actor from discovery to service once any
22    /// discovered floor has been consumed. Service answers peers' `Request` from the
23    /// attached marshal and never issues outbound requests.
24    Attach {
25        /// The marshal mailbox to serve the latest finalization from.
26        marshal: MarshalMailbox<S, V>,
27    },
28}
29
30impl<S, V> Policy for Message<S, V>
31where
32    S: Scheme,
33    V: Variant,
34{
35    type Overflow = VecDeque<Self>;
36
37    fn handle(overflow: &mut Self::Overflow, message: Self) {
38        overflow.push_back(message);
39    }
40}
41
42/// Handle to the mailbox of the [`Probe`](super::Probe).
43#[derive(Clone)]
44pub struct Mailbox<S, V>
45where
46    S: Scheme,
47    V: Variant,
48{
49    sender: Sender<Message<S, V>>,
50}
51
52impl<S, V> Mailbox<S, V>
53where
54    S: Scheme,
55    V: Variant,
56{
57    pub(crate) const fn new(sender: Sender<Message<S, V>>) -> Self {
58        Self { sender }
59    }
60
61    /// Open a subscription to the receipt of the floor finalization from peers.
62    ///
63    /// While the actor is still discovering, this requests discovery if no floor has been selected
64    /// yet. Dropping the receiver cancels this subscription; if all subscribers are dropped before
65    /// a floor is selected, discovery may be abandoned. If marshal is later attached, the actor
66    /// transitions to service without a cached floor and later subscriptions will not restart
67    /// discovery.
68    ///
69    /// Callers that need a floor must keep the receiver alive until it resolves and should attach
70    /// only after consuming that floor.
71    ///
72    /// If a floor has already been selected, the receiver resolves immediately.
73    pub fn subscribe(&self) -> oneshot::Receiver<Finalization<S, V::Commitment>> {
74        let (tx, rx) = oneshot::channel();
75        let _ = self.sender.enqueue(Message::Subscribe { response: tx });
76        rx
77    }
78
79    /// Attach a marshal mailbox so the actor can serve the latest finalization to peers.
80    ///
81    /// This moves the actor from discovery to service. It is applied only after any
82    /// discovered floor has been delivered to its subscribers. If no floor was ever requested, or
83    /// every pending subscriber was dropped before a floor was selected, the actor serves without a
84    /// cached floor.
85    pub fn attach(&self, marshal: MarshalMailbox<S, V>) {
86        let _ = self.sender.enqueue(Message::Attach { marshal });
87    }
88}