|
@@ -2,15 +2,15 @@
|
|
// This includes inducing transitions and actions taken during transitions,
|
|
// This includes inducing transitions and actions taken during transitions,
|
|
// so a lot of the messenger network code is here.
|
|
// so a lot of the messenger network code is here.
|
|
|
|
|
|
-use mgen::log;
|
|
|
|
|
|
+use mgen::{log, MessageHeader, SerializedMessage};
|
|
use rand_distr::Distribution;
|
|
use rand_distr::Distribution;
|
|
use rand_xoshiro::Xoshiro256PlusPlus;
|
|
use rand_xoshiro::Xoshiro256PlusPlus;
|
|
-use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
|
|
-use tokio::net::TcpStream;
|
|
|
|
|
|
+use std::collections::HashMap;
|
|
|
|
+use std::sync::Arc;
|
|
|
|
+use tokio::sync::mpsc;
|
|
use tokio::time::Instant;
|
|
use tokio::time::Instant;
|
|
|
|
|
|
use crate::messenger::dists::Distributions;
|
|
use crate::messenger::dists::Distributions;
|
|
-use crate::messenger::error::MessengerError;
|
|
|
|
use crate::messenger::message::{construct_message, construct_receipt};
|
|
use crate::messenger::message::{construct_message, construct_receipt};
|
|
|
|
|
|
/// All possible Conversation state machine states
|
|
/// All possible Conversation state machine states
|
|
@@ -147,48 +147,59 @@ impl Conversation<Active> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- async fn sleep(delay: Instant, wait: Instant) -> ActiveActions {
|
|
|
|
|
|
+ async fn sleep(delay: Instant, wait: Instant) -> ActiveGroupActions {
|
|
if delay < wait {
|
|
if delay < wait {
|
|
log!("delaying for {:?}", delay - Instant::now());
|
|
log!("delaying for {:?}", delay - Instant::now());
|
|
tokio::time::sleep_until(delay).await;
|
|
tokio::time::sleep_until(delay).await;
|
|
- ActiveActions::Send
|
|
|
|
|
|
+ ActiveGroupActions::Send
|
|
} else {
|
|
} else {
|
|
log!("waiting for {:?}", wait - Instant::now());
|
|
log!("waiting for {:?}", wait - Instant::now());
|
|
tokio::time::sleep_until(wait).await;
|
|
tokio::time::sleep_until(wait).await;
|
|
- ActiveActions::Idle
|
|
|
|
|
|
+ ActiveGroupActions::Idle
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-/// Attempt to read some portion of the header size from the stream.
|
|
|
|
-/// The number of bytes written is returned in the Ok case.
|
|
|
|
-/// The caller must read any remaining bytes less than 4.
|
|
|
|
-// N.B.: This must be written cancellation safe!
|
|
|
|
-// https://docs.rs/tokio/1.26.0/tokio/macro.select.html#cancellation-safety
|
|
|
|
-async fn read_header_size(
|
|
|
|
- stream: &mut TcpStream,
|
|
|
|
- header_size: &mut [u8; 4],
|
|
|
|
-) -> Result<usize, MessengerError> {
|
|
|
|
- let read = stream.read(header_size).await?;
|
|
|
|
-
|
|
|
|
- if read == 0 {
|
|
|
|
- Err(tokio::io::Error::new(
|
|
|
|
- tokio::io::ErrorKind::WriteZero,
|
|
|
|
- "failed to read any bytes from message with bytes remaining",
|
|
|
|
- )
|
|
|
|
- .into())
|
|
|
|
- } else {
|
|
|
|
- Ok(read)
|
|
|
|
|
|
+/// Type for getting messages from the reader thread in the state thread.
|
|
|
|
+type StateFromReader = mpsc::UnboundedReceiver<MessageHeader>;
|
|
|
|
+/// Type for sending messages from the state thread to the writer thread.
|
|
|
|
+type StateToWriter = mpsc::UnboundedSender<Arc<SerializedMessage>>;
|
|
|
|
+
|
|
|
|
+pub trait StreamMap<'a, I: Iterator<Item = &'a mut StateToWriter>> {
|
|
|
|
+ fn channel_for(&self, name: &str) -> &StateToWriter;
|
|
|
|
+ fn values(&'a mut self) -> I;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl<'a> StreamMap<'a, std::collections::hash_map::ValuesMut<'a, String, StateToWriter>>
|
|
|
|
+ for HashMap<String, StateToWriter>
|
|
|
|
+{
|
|
|
|
+ fn channel_for(&self, name: &str) -> &StateToWriter {
|
|
|
|
+ &self[name]
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn values(&'a mut self) -> std::collections::hash_map::ValuesMut<'a, String, StateToWriter> {
|
|
|
|
+ self.values_mut()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-async fn send_action<T: State>(
|
|
|
|
|
|
+impl<'a> StreamMap<'a, std::iter::Once<&'a mut StateToWriter>> for StateToWriter {
|
|
|
|
+ fn channel_for(&self, _name: &str) -> &StateToWriter {
|
|
|
|
+ self
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn values(&'a mut self) -> std::iter::Once<&'a mut StateToWriter> {
|
|
|
|
+ std::iter::once(self)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+async fn send_action<'a, T: State, I: Iterator<Item = &'a mut StateToWriter>>(
|
|
conversation: Conversation<T>,
|
|
conversation: Conversation<T>,
|
|
- stream: &mut TcpStream,
|
|
|
|
|
|
+ streams: I,
|
|
our_id: &str,
|
|
our_id: &str,
|
|
|
|
+ group: &str,
|
|
recipients: Vec<&str>,
|
|
recipients: Vec<&str>,
|
|
rng: &mut Xoshiro256PlusPlus,
|
|
rng: &mut Xoshiro256PlusPlus,
|
|
-) -> Result<StateMachine, (StateMachine, MessengerError)> {
|
|
|
|
|
|
+) -> StateMachine {
|
|
let size = conversation.dists.m.sample(rng);
|
|
let size = conversation.dists.m.sample(rng);
|
|
log!(
|
|
log!(
|
|
"sending message from {} to {:?} of size {}",
|
|
"sending message from {} to {:?} of size {}",
|
|
@@ -196,42 +207,35 @@ async fn send_action<T: State>(
|
|
recipients,
|
|
recipients,
|
|
size
|
|
size
|
|
);
|
|
);
|
|
- let m = construct_message(
|
|
|
|
|
|
+ let m = Arc::new(construct_message(
|
|
our_id.to_string(),
|
|
our_id.to_string(),
|
|
|
|
+ group.to_string(),
|
|
recipients.iter().map(|s| s.to_string()).collect(),
|
|
recipients.iter().map(|s| s.to_string()).collect(),
|
|
size,
|
|
size,
|
|
- );
|
|
|
|
|
|
+ ));
|
|
|
|
|
|
- if let Err(e) = m.write_all_to(stream).await {
|
|
|
|
- return Err((T::to_machine(conversation), e.into()));
|
|
|
|
- }
|
|
|
|
- if let Err(e) = stream.flush().await {
|
|
|
|
- return Err((T::to_machine(conversation), e.into()));
|
|
|
|
|
|
+ for stream in streams {
|
|
|
|
+ stream
|
|
|
|
+ .send(m.clone())
|
|
|
|
+ .expect("Internal stream closed with messages still being sent");
|
|
}
|
|
}
|
|
|
|
|
|
- Ok(T::sent(conversation, rng))
|
|
|
|
|
|
+ T::sent(conversation, rng)
|
|
}
|
|
}
|
|
|
|
|
|
-async fn receive_action<T: State>(
|
|
|
|
- n: usize,
|
|
|
|
- mut header_size: [u8; 4],
|
|
|
|
|
|
+async fn receive_action<
|
|
|
|
+ 'a,
|
|
|
|
+ T: State,
|
|
|
|
+ I: std::iter::Iterator<Item = &'a mut StateToWriter>,
|
|
|
|
+ M: StreamMap<'a, I>,
|
|
|
|
+>(
|
|
|
|
+ msg: MessageHeader,
|
|
conversation: Conversation<T>,
|
|
conversation: Conversation<T>,
|
|
- stream: &mut TcpStream,
|
|
|
|
|
|
+ stream_map: &mut M,
|
|
our_id: &str,
|
|
our_id: &str,
|
|
|
|
+ group: &str,
|
|
rng: &mut Xoshiro256PlusPlus,
|
|
rng: &mut Xoshiro256PlusPlus,
|
|
-) -> Result<StateMachine, (StateMachine, MessengerError)> {
|
|
|
|
- if n < 4 {
|
|
|
|
- // we didn't get the whole size, but we can use read_exact now
|
|
|
|
- if let Err(e) = stream.read_exact(&mut header_size[n..]).await {
|
|
|
|
- return Err((T::to_machine(conversation), e.into()));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- let msg = match mgen::get_message_with_header_size(stream, header_size).await {
|
|
|
|
- Ok((msg, _)) => msg,
|
|
|
|
- Err(e) => return Err((T::to_machine(conversation), e.into())),
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
|
|
+) -> StateMachine {
|
|
match msg.body {
|
|
match msg.body {
|
|
mgen::MessageBody::Size(size) => {
|
|
mgen::MessageBody::Size(size) => {
|
|
log!(
|
|
log!(
|
|
@@ -240,94 +244,105 @@ async fn receive_action<T: State>(
|
|
msg.sender,
|
|
msg.sender,
|
|
size
|
|
size
|
|
);
|
|
);
|
|
- let m = construct_receipt(our_id.to_string(), msg.sender);
|
|
|
|
- if let Err(e) = m.write_all_to(stream).await {
|
|
|
|
- return Err((T::to_machine(conversation), e.into()));
|
|
|
|
- }
|
|
|
|
- if let Err(e) = stream.flush().await {
|
|
|
|
- return Err((T::to_machine(conversation), e.into()));
|
|
|
|
- }
|
|
|
|
- Ok(T::received(conversation, rng))
|
|
|
|
|
|
+ let stream = stream_map.channel_for(&msg.sender);
|
|
|
|
+ let m = construct_receipt(our_id.to_string(), group.to_string(), msg.sender);
|
|
|
|
+ stream
|
|
|
|
+ .send(Arc::new(m))
|
|
|
|
+ .expect("channel from receive_action to sender closed");
|
|
|
|
+ T::received(conversation, rng)
|
|
}
|
|
}
|
|
- mgen::MessageBody::Receipt => Ok(T::to_machine(conversation)),
|
|
|
|
|
|
+ mgen::MessageBody::Receipt => T::to_machine(conversation),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-enum IdleActions {
|
|
|
|
|
|
+enum IdleGroupActions {
|
|
Send,
|
|
Send,
|
|
- Receive(usize),
|
|
|
|
|
|
+ Receive(MessageHeader),
|
|
}
|
|
}
|
|
|
|
|
|
-pub async fn manage_idle_conversation(
|
|
|
|
|
|
+/// Handle a state transition from Idle, including I/O, for a multi-connection conversation.
|
|
|
|
+/// Used for Idle group p2p conversations.
|
|
|
|
+pub async fn manage_idle_conversation<
|
|
|
|
+ 'a,
|
|
|
|
+ I: std::iter::Iterator<Item = &'a mut StateToWriter>,
|
|
|
|
+ M: StreamMap<'a, I> + 'a,
|
|
|
|
+>(
|
|
conversation: Conversation<Idle>,
|
|
conversation: Conversation<Idle>,
|
|
- stream: &mut TcpStream,
|
|
|
|
|
|
+ inbound: &mut StateFromReader,
|
|
|
|
+ stream_map: &'a mut M,
|
|
our_id: &str,
|
|
our_id: &str,
|
|
|
|
+ group: &str,
|
|
recipients: Vec<&str>,
|
|
recipients: Vec<&str>,
|
|
rng: &mut Xoshiro256PlusPlus,
|
|
rng: &mut Xoshiro256PlusPlus,
|
|
-) -> Result<StateMachine, (StateMachine, MessengerError)> {
|
|
|
|
|
|
+) -> StateMachine {
|
|
log!("delaying for {:?}", conversation.delay - Instant::now());
|
|
log!("delaying for {:?}", conversation.delay - Instant::now());
|
|
- let mut header_size = [0; 4];
|
|
|
|
let action = tokio::select! {
|
|
let action = tokio::select! {
|
|
- () = tokio::time::sleep_until(conversation.delay) => {
|
|
|
|
- Ok(IdleActions::Send)
|
|
|
|
- }
|
|
|
|
|
|
+ () = tokio::time::sleep_until(conversation.delay) => IdleGroupActions::Send,
|
|
|
|
|
|
- res = read_header_size(stream, &mut header_size) => {
|
|
|
|
- match res {
|
|
|
|
- Ok(n) => Ok(IdleActions::Receive(n)),
|
|
|
|
- Err(e) => Err(e),
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
- let action = match action {
|
|
|
|
- Ok(action) => action,
|
|
|
|
- Err(e) => return Err((StateMachine::Idle(conversation), e)),
|
|
|
|
|
|
+ res = inbound.recv() =>
|
|
|
|
+ IdleGroupActions::Receive(res.expect("inbound channel closed")),
|
|
};
|
|
};
|
|
|
|
|
|
match action {
|
|
match action {
|
|
- IdleActions::Send => send_action(conversation, stream, our_id, recipients, rng).await,
|
|
|
|
- IdleActions::Receive(n) => {
|
|
|
|
- receive_action(n, header_size, conversation, stream, our_id, rng).await
|
|
|
|
|
|
+ IdleGroupActions::Send => {
|
|
|
|
+ send_action(
|
|
|
|
+ conversation,
|
|
|
|
+ stream_map.values(),
|
|
|
|
+ our_id,
|
|
|
|
+ group,
|
|
|
|
+ recipients,
|
|
|
|
+ rng,
|
|
|
|
+ )
|
|
|
|
+ .await
|
|
|
|
+ }
|
|
|
|
+ IdleGroupActions::Receive(msg) => {
|
|
|
|
+ receive_action(msg, conversation, stream_map, our_id, group, rng).await
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-enum ActiveActions {
|
|
|
|
|
|
+enum ActiveGroupActions {
|
|
Send,
|
|
Send,
|
|
- Receive(usize),
|
|
|
|
|
|
+ Receive(MessageHeader),
|
|
Idle,
|
|
Idle,
|
|
}
|
|
}
|
|
|
|
|
|
-pub async fn manage_active_conversation(
|
|
|
|
|
|
+/// Handle a state transition from Active.
|
|
|
|
+pub async fn manage_active_conversation<
|
|
|
|
+ 'a,
|
|
|
|
+ I: std::iter::Iterator<Item = &'a mut StateToWriter>,
|
|
|
|
+ M: StreamMap<'a, I> + 'a,
|
|
|
|
+>(
|
|
conversation: Conversation<Active>,
|
|
conversation: Conversation<Active>,
|
|
- stream: &mut TcpStream,
|
|
|
|
|
|
+ inbound: &mut StateFromReader,
|
|
|
|
+ stream_map: &'a mut M,
|
|
our_id: &str,
|
|
our_id: &str,
|
|
|
|
+ group: &str,
|
|
recipients: Vec<&str>,
|
|
recipients: Vec<&str>,
|
|
rng: &mut Xoshiro256PlusPlus,
|
|
rng: &mut Xoshiro256PlusPlus,
|
|
-) -> Result<StateMachine, (StateMachine, MessengerError)> {
|
|
|
|
- let mut header_size = [0; 4];
|
|
|
|
|
|
+) -> StateMachine {
|
|
let action = tokio::select! {
|
|
let action = tokio::select! {
|
|
- action = Conversation::<Active>::sleep(conversation.delay, conversation.state.wait) => {
|
|
|
|
- Ok(action)
|
|
|
|
- }
|
|
|
|
|
|
+ action = Conversation::<Active>::sleep(conversation.delay, conversation.state.wait) => action,
|
|
|
|
|
|
- res = read_header_size(stream, &mut header_size) => {
|
|
|
|
- match res {
|
|
|
|
- Ok(n) => Ok(ActiveActions::Receive(n)),
|
|
|
|
- Err(e) => Err(e),
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
- let action = match action {
|
|
|
|
- Ok(action) => action,
|
|
|
|
- Err(e) => return Err((StateMachine::Active(conversation), e)),
|
|
|
|
|
|
+ res = inbound.recv() =>
|
|
|
|
+ ActiveGroupActions::Receive(res.expect("inbound channel closed")),
|
|
};
|
|
};
|
|
|
|
|
|
match action {
|
|
match action {
|
|
- ActiveActions::Send => send_action(conversation, stream, our_id, recipients, rng).await,
|
|
|
|
- ActiveActions::Receive(n) => {
|
|
|
|
- receive_action(n, header_size, conversation, stream, our_id, rng).await
|
|
|
|
|
|
+ ActiveGroupActions::Send => {
|
|
|
|
+ send_action(
|
|
|
|
+ conversation,
|
|
|
|
+ stream_map.values(),
|
|
|
|
+ our_id,
|
|
|
|
+ group,
|
|
|
|
+ recipients,
|
|
|
|
+ rng,
|
|
|
|
+ )
|
|
|
|
+ .await
|
|
|
|
+ }
|
|
|
|
+ ActiveGroupActions::Receive(msg) => {
|
|
|
|
+ receive_action(msg, conversation, stream_map, our_id, group, rng).await
|
|
}
|
|
}
|
|
- ActiveActions::Idle => Ok(StateMachine::Idle(conversation.waited(rng))),
|
|
|
|
|
|
+ ActiveGroupActions::Idle => StateMachine::Idle(conversation.waited(rng)),
|
|
}
|
|
}
|
|
}
|
|
}
|