use commonware_actor::mailbox::{Policy, Sender}; use commonware_consensus::{ marshal::core::{Mailbox as MarshalMailbox, Variant}, simplex::types::Finalization, }; use commonware_cryptography::certificate::Scheme; use commonware_utils::channel::oneshot; use std::collections::VecDeque; /// A message that can be sent to the [`Probe`](super::Probe). pub(crate) enum Message where S: Scheme, V: Variant, { /// Subscribe for the receipt of the floor finalization from peers. Subscribe { /// The response channel to send the finalization to. response: oneshot::Sender>, }, /// Attach a marshal mailbox, moving the actor from discovery to service once any /// discovered floor has been consumed. Service answers peers' `Request` from the /// attached marshal and never issues outbound requests. Attach { /// The marshal mailbox to serve the latest finalization from. marshal: MarshalMailbox, }, } impl Policy for Message where S: Scheme, V: Variant, { type Overflow = VecDeque; fn handle(overflow: &mut Self::Overflow, message: Self) { overflow.push_back(message); } } /// Handle to the mailbox of the [`Probe`](super::Probe). #[derive(Clone)] pub struct Mailbox where S: Scheme, V: Variant, { sender: Sender>, } impl Mailbox where S: Scheme, V: Variant, { pub(crate) const fn new(sender: Sender>) -> Self { Self { sender } } /// Open a subscription to the receipt of the floor finalization from peers. /// /// While the actor is still discovering, this requests discovery if no floor has been selected /// yet. Dropping the receiver cancels this subscription; if all subscribers are dropped before /// a floor is selected, discovery may be abandoned. If marshal is later attached, the actor /// transitions to service without a cached floor and later subscriptions will not restart /// discovery. /// /// Callers that need a floor must keep the receiver alive until it resolves and should attach /// only after consuming that floor. /// /// If a floor has already been selected, the receiver resolves immediately. pub fn subscribe(&self) -> oneshot::Receiver> { let (tx, rx) = oneshot::channel(); let _ = self.sender.enqueue(Message::Subscribe { response: tx }); rx } /// Attach a marshal mailbox so the actor can serve the latest finalization to peers. /// /// This moves the actor from discovery to service. It is applied only after any /// discovered floor has been delivered to its subscribers. If no floor was ever requested, or /// every pending subscriber was dropped before a floor was selected, the actor serves without a /// cached floor. pub fn attach(&self, marshal: MarshalMailbox) { let _ = self.sender.enqueue(Message::Attach { marshal }); } }