use crate::{
Channel, Recipients,
authenticated::{channels::Channels, data::EncodedData, relay::Relay},
utils::limited::Connected,
};
use commonware_actor::{
Feedback, Unreliable,
mailbox::{self, UnreliablePolicy},
};
use commonware_cryptography::PublicKey;
use commonware_runtime::{BufferPool, IoBufs};
use commonware_utils::{
NZUsize,
channel::{oneshot, ring},
sync::Mutex,
};
use std::{
collections::VecDeque,
fmt,
ops::Deref,
sync::{Arc, OnceLock},
};
pub enum Message<P: PublicKey> {
Ready {
peer: P,
relay: Relay<EncodedData>,
channels: oneshot::Sender<Channels<P>>,
},
Release { peer: P },
Content {
recipients: Recipients<P>,
encoded: EncodedData,
priority: bool,
},
SubscribePeers { sender: ring::Sender<Vec<P>> },
}
impl<P: PublicKey> UnreliablePolicy for Message<P> {
type Overflow = VecDeque<Self>;
fn handle(overflow: &mut Self::Overflow, message: Self) -> bool {
match message {
Self::Content { .. } => false,
message => {
overflow.push_back(message);
true
}
}
}
}
pub struct Mailbox<P: PublicKey>(mailbox::UnreliableSender<Message<P>>);
impl<P: PublicKey> Clone for Mailbox<P> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<P: PublicKey> fmt::Debug for Mailbox<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Mailbox").field(&self.0).finish()
}
}
impl<P: PublicKey> Mailbox<P> {
pub const fn new(sender: mailbox::UnreliableSender<Message<P>>) -> Self {
Self(sender)
}
pub async fn ready(&self, peer: P, relay: Relay<EncodedData>) -> Option<Channels<P>> {
let (channels, receiver) = oneshot::channel();
let _ = self.0.enqueue(Message::Ready {
peer,
relay,
channels,
});
receiver.await.ok()
}
pub fn release(&self, peer: P) -> Feedback {
match self.0.enqueue(Message::Release { peer }) {
Unreliable::Outcome(feedback) => feedback,
Unreliable::Rejected => unreachable!("router release cannot be rejected"),
}
}
}
struct Shared<P: PublicKey> {
mailbox: OnceLock<Option<Mailbox<P>>>,
pending_subscriptions: Mutex<Vec<ring::Sender<Vec<P>>>>,
}
impl<P: PublicKey> Shared<P> {
const fn new() -> Self {
Self {
mailbox: OnceLock::new(),
pending_subscriptions: Mutex::new(Vec::new()),
}
}
fn bind(&self, mailbox: Mailbox<P>) {
assert!(
self.mailbox.set(Some(mailbox)).is_ok(),
"router messenger already bound or closed"
);
let mut pending_subscriptions = self.pending_subscriptions.lock();
let mailbox = self.mailbox.get().unwrap().as_ref().unwrap();
for sender in pending_subscriptions.drain(..) {
let _ = mailbox.0.enqueue(Message::SubscribePeers { sender });
}
}
}
#[derive(Clone)]
pub struct Messenger<P: PublicKey> {
pool: BufferPool,
shared: Arc<Shared<P>>,
}
#[derive(Debug)]
pub(in crate::authenticated) struct OwnedMessenger<P: PublicKey> {
messenger: Messenger<P>,
}
impl<P: PublicKey> Drop for OwnedMessenger<P> {
fn drop(&mut self) {
let shared = &self.messenger.shared;
if shared.mailbox.set(None).is_ok() {
shared.pending_subscriptions.lock().clear();
}
}
}
impl<P: PublicKey> OwnedMessenger<P> {
pub(in crate::authenticated) fn handle(&self) -> Messenger<P> {
self.messenger.clone()
}
pub(in crate::authenticated) fn bind(&self, mailbox: Mailbox<P>) {
self.messenger.shared.bind(mailbox);
}
}
impl<P: PublicKey> Deref for OwnedMessenger<P> {
type Target = Messenger<P>;
fn deref(&self) -> &Self::Target {
&self.messenger
}
}
impl<P: PublicKey> fmt::Debug for Messenger<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mailbox = self.shared.mailbox.get();
f.debug_struct("Messenger")
.field("bound", &mailbox.is_some_and(Option::is_some))
.field("closed", &mailbox.is_some_and(Option::is_none))
.finish_non_exhaustive()
}
}
impl<P: PublicKey> Messenger<P> {
pub(in crate::authenticated) fn unbound(pool: BufferPool) -> Arc<OwnedMessenger<P>> {
let messenger = Self {
pool,
shared: Arc::new(Shared::new()),
};
Arc::new(OwnedMessenger { messenger })
}
pub fn content(
&self,
recipients: Recipients<P>,
channel: Channel,
message: IoBufs,
priority: bool,
) -> Unreliable<Feedback> {
let Some(mailbox) = self.shared.mailbox.get() else {
return Unreliable::new(Feedback::Ok);
};
let Some(mailbox) = mailbox else {
return Unreliable::new(Feedback::Closed);
};
let encoded = EncodedData::new(&self.pool, channel, message);
mailbox.0.enqueue(Message::Content {
recipients,
encoded,
priority,
})
}
}
impl<P: PublicKey> Connected for Messenger<P> {
type PublicKey = P;
fn subscribe(&self) -> ring::Receiver<Vec<Self::PublicKey>> {
let (sender, receiver) = ring::channel(NZUsize!(1));
if let Some(mailbox) = self.shared.mailbox.get() {
if let Some(mailbox) = mailbox {
let _ = mailbox.0.enqueue(Message::SubscribePeers { sender });
}
return receiver;
}
let mut pending_subscriptions = self.shared.pending_subscriptions.lock();
match self.shared.mailbox.get() {
Some(Some(mailbox)) => {
drop(pending_subscriptions);
let _ = mailbox.0.enqueue(Message::SubscribePeers { sender });
}
Some(None) => {}
None => pending_subscriptions.push(sender),
}
receiver
}
}
#[cfg(test)]
mod tests {
use super::*;
use commonware_cryptography::{
Signer as _,
ed25519::{PrivateKey, PublicKey},
};
use commonware_runtime::{
BufferPooler as _, IoBuf, Runner as _, Supervisor as _, deterministic,
};
#[test]
fn test_unbound_messenger_accepts_and_drops_content() {
let executor = deterministic::Runner::default();
executor.start(|context| async move {
let messenger = Messenger::<PublicKey>::unbound(context.network_buffer_pool().clone());
assert_eq!(
messenger.content(Recipients::All, 7, IoBuf::from(b"message").into(), false),
Unreliable::new(Feedback::Ok)
);
let (sender, mut receiver) = mailbox::new_unreliable::<Message<PublicKey>>(
context.child("router_mailbox"),
NZUsize!(1),
);
messenger.bind(Mailbox::new(sender));
assert!(receiver.try_recv().is_err());
});
}
#[test]
fn test_unbound_messenger_defers_subscriptions() {
let executor = deterministic::Runner::default();
executor.start(|context| async move {
let messenger = Messenger::<PublicKey>::unbound(context.network_buffer_pool().clone());
let _subscription = messenger.subscribe();
let (sender, mut receiver) = mailbox::new_unreliable::<Message<PublicKey>>(
context.child("router_mailbox"),
NZUsize!(1),
);
messenger.bind(Mailbox::new(sender));
assert!(matches!(
receiver.try_recv(),
Ok(Message::SubscribePeers { .. })
));
});
}
#[test]
fn test_overflow_rejects_content_but_retains_control() {
let executor = deterministic::Runner::default();
executor.start(|context| async move {
let (control_sender, mut receiver) = mailbox::new_unreliable::<Message<PublicKey>>(
context.child("control_mailbox"),
NZUsize!(1),
);
let mailbox = Mailbox::new(control_sender.clone());
let messenger = Messenger::unbound(context.network_buffer_pool().clone());
messenger.bind(Mailbox::new(control_sender));
let peer = PrivateKey::from_seed(1).public_key();
assert_eq!(
messenger.content(
Recipients::One(peer.clone()),
7,
IoBuf::from(b"one").into(),
false
),
Unreliable::new(Feedback::Ok)
);
assert_eq!(
messenger.content(Recipients::One(peer), 7, IoBuf::from(b"two").into(), false),
Unreliable::Rejected
);
assert_eq!(
mailbox.release(PrivateKey::from_seed(2).public_key()),
Feedback::Backoff
);
match receiver.try_recv().unwrap() {
Message::Content { encoded, .. } => assert_eq!(encoded.channel, 7),
_ => panic!("expected content"),
}
match receiver.try_recv().unwrap() {
Message::Release { .. } => {}
_ => panic!("expected release"),
}
assert!(receiver.try_recv().is_err());
});
}
}