use std::{
collections::HashMap,
marker::PhantomData,
time::{Duration, Instant},
};
use log::{debug, info, trace};
use crate::{
network::{
session::{Authentication, SessionHandler},
AddressingInformation,
},
NodeIndex,
};
pub struct Discovery<A: AddressingInformation> {
cooldown: Duration,
last_broadcast: HashMap<NodeIndex, Instant>,
_phantom: PhantomData<A>,
}
impl<A: AddressingInformation> Discovery<A> {
pub fn new(cooldown: Duration) -> Self {
Discovery {
cooldown,
last_broadcast: HashMap::new(),
_phantom: PhantomData,
}
}
pub fn discover_authorities(
&mut self,
handler: &SessionHandler<A>,
) -> Option<Authentication<A>> {
let authentication = match handler.authentication() {
Some(authentication) => authentication,
None => return None,
};
let missing_authorities = handler.missing_nodes();
let node_count = handler.node_count();
info!(target: "phron-network", "{}/{} authorities known for session {}.", node_count.0-missing_authorities.len(), node_count.0, handler.session_id().0);
Some(authentication)
}
fn should_rebroadcast(&self, node_id: &NodeIndex) -> bool {
match self.last_broadcast.get(node_id) {
Some(instant) => Instant::now() > *instant + self.cooldown,
None => true,
}
}
pub fn handle_authentication(
&mut self,
authentication: Authentication<A>,
handler: &mut SessionHandler<A>,
) -> (Option<A>, Option<Authentication<A>>) {
debug!(target: "phron-network", "Handling broadcast with authentication {:?}.", authentication);
let address = match handler.handle_authentication(authentication.clone()) {
Some(address) => Some(address),
None => return (None, None),
};
let node_id = authentication.0.creator();
if !self.should_rebroadcast(&node_id) {
return (address, None);
}
trace!(target: "phron-network", "Rebroadcasting {:?}.", authentication);
self.last_broadcast.insert(node_id, Instant::now());
(address, Some(authentication))
}
}
#[cfg(test)]
mod tests {
use std::{thread::sleep, time::Duration};
use network_clique::mock::{random_address, MockAddressingInformation};
use super::Discovery;
use crate::{
network::{
mock::crypto_basics,
session::{authentication, Authentication, SessionHandler},
},
SessionId,
};
const NUM_NODES: u8 = 7;
const MS_COOLDOWN: u64 = 200;
fn addresses() -> Vec<MockAddressingInformation> {
(0..NUM_NODES).map(|_| random_address()).collect()
}
fn build_number(
num_nodes: u8,
) -> (
Discovery<MockAddressingInformation>,
Vec<SessionHandler<MockAddressingInformation>>,
SessionHandler<MockAddressingInformation>,
) {
let crypto_basics = crypto_basics(num_nodes.into());
let mut handlers = Vec::new();
for (authority_index_and_pen, address) in crypto_basics.0.into_iter().zip(addresses()) {
handlers.push(SessionHandler::new(
Some(authority_index_and_pen),
crypto_basics.1.clone(),
SessionId(43),
address,
));
}
let non_validator =
SessionHandler::new(None, crypto_basics.1, SessionId(43), random_address());
(
Discovery::new(Duration::from_millis(MS_COOLDOWN)),
handlers,
non_validator,
)
}
fn build() -> (
Discovery<MockAddressingInformation>,
Vec<SessionHandler<MockAddressingInformation>>,
SessionHandler<MockAddressingInformation>,
) {
build_number(NUM_NODES)
}
#[test]
fn broadcasts_when_clueless() {
for num_nodes in 2..NUM_NODES {
let (mut discovery, mut handlers, _) = build_number(num_nodes);
let handler = &mut handlers[0];
let maybe_authentication = discovery.discover_authorities(handler);
assert_eq!(
maybe_authentication.expect("there is an authentication"),
handler
.authentication()
.expect("the handler has an authentication"),
);
}
}
#[test]
fn non_validator_discover_authorities_returns_empty_vector() {
let (mut discovery, _, non_validator) = build();
let maybe_authentication = discovery.discover_authorities(&non_validator);
assert!(maybe_authentication.is_none());
}
#[test]
fn rebroadcasts_and_accepts_addresses() {
let (mut discovery, mut handlers, _) = build();
let authentication = authentication(&handlers[1]);
let handler = &mut handlers[0];
let (address, command) = discovery.handle_authentication(authentication.clone(), handler);
assert_eq!(address, Some(authentication.0.address()));
assert!(matches!(command, Some(
rebroadcast_authentication,
) if rebroadcast_authentication == authentication));
}
#[test]
fn non_validator_rebroadcasts() {
let (mut discovery, handlers, mut non_validator) = build();
let authentication = authentication(&handlers[1]);
let (address, command) =
discovery.handle_authentication(authentication.clone(), &mut non_validator);
assert_eq!(address, Some(authentication.0.address()));
assert!(matches!(command, Some(
rebroadcast_authentication,
) if rebroadcast_authentication == authentication));
}
#[test]
fn does_not_rebroadcast_wrong_authentications() {
let (mut discovery, mut handlers, _) = build();
let Authentication(auth_data, _) = authentication(&handlers[1]);
let Authentication(_, signature) = authentication(&handlers[2]);
let authentication = Authentication(auth_data, signature);
let handler = &mut handlers[0];
let (address, command) = discovery.handle_authentication(authentication, handler);
assert!(address.is_none());
assert!(command.is_none());
}
#[test]
fn rebroadcasts_after_cooldown() {
let (mut discovery, mut handlers, _) = build();
let authentication = authentication(&handlers[1]);
let handler = &mut handlers[0];
discovery.handle_authentication(authentication.clone(), handler);
sleep(Duration::from_millis(MS_COOLDOWN + 5));
let (address, command) = discovery.handle_authentication(authentication.clone(), handler);
assert_eq!(address, Some(authentication.0.address()));
assert!(matches!(command, Some(
rebroadcast_authentication,
) if rebroadcast_authentication == authentication));
}
}