use super::SectionAuthorityProviderUtils;
use crate::{peer::PeerUtils, section::NodeStateUtils};
use itertools::Itertools;
use sn_messaging::{
node::{MembershipState, NodeState, Peer, SectionPeers, SectionSigned},
SectionAuthorityProvider,
};
use std::{
cmp::Ordering,
collections::btree_map::{self, Entry},
mem,
};
use xor_name::{Prefix, XorName};
pub trait SectionPeersUtils {
fn all(&self) -> Box<dyn Iterator<Item = &NodeState> + '_>;
fn joined(&self) -> Box<dyn Iterator<Item = &NodeState> + '_>;
fn mature(&self) -> Box<dyn Iterator<Item = &Peer> + '_>;
fn get(&self, name: &XorName) -> Option<&NodeState>;
fn get_section_signed(&self, name: &XorName) -> Option<&SectionSigned<NodeState>>;
fn elder_candidates(
&self,
elder_size: usize,
current_elders: &SectionAuthorityProvider,
) -> Vec<Peer>;
fn elder_candidates_matching_prefix(
&self,
prefix: &Prefix,
elder_size: usize,
current_elders: &SectionAuthorityProvider,
) -> Vec<Peer>;
fn is_joined(&self, name: &XorName) -> bool;
fn update(&mut self, new_info: SectionSigned<NodeState>) -> bool;
fn prune_not_matching(&mut self, prefix: &Prefix);
}
impl SectionPeersUtils for SectionPeers {
fn all(&self) -> Box<dyn Iterator<Item = &NodeState> + '_> {
Box::new(self.members.values().map(|info| &info.value))
}
fn joined(&self) -> Box<dyn Iterator<Item = &NodeState> + '_> {
Box::new(
self.members
.values()
.map(|info| &info.value)
.filter(|member| member.state == MembershipState::Joined),
)
}
fn mature(&self) -> Box<dyn Iterator<Item = &Peer> + '_> {
Box::new(
self.joined()
.filter(|info| info.is_mature())
.map(|info| &info.peer),
)
}
fn get(&self, name: &XorName) -> Option<&NodeState> {
self.members.get(name).map(|info| &info.value)
}
fn get_section_signed(&self, name: &XorName) -> Option<&SectionSigned<NodeState>> {
self.members.get(name)
}
fn elder_candidates(
&self,
elder_size: usize,
current_elders: &SectionAuthorityProvider,
) -> Vec<Peer> {
elder_candidates(
elder_size,
current_elders,
self.members
.values()
.filter(|info| is_active(&info.value, current_elders))
.filter(|info| info.value.peer.is_reachable()),
)
}
fn elder_candidates_matching_prefix(
&self,
prefix: &Prefix,
elder_size: usize,
current_elders: &SectionAuthorityProvider,
) -> Vec<Peer> {
elder_candidates(
elder_size,
current_elders,
self.members.values().filter(|info| {
info.value.state == MembershipState::Joined
&& prefix.matches(info.value.peer.name())
&& info.value.peer.is_reachable()
}),
)
}
fn is_joined(&self, name: &XorName) -> bool {
self.members
.get(name)
.map(|info| info.value.state == MembershipState::Joined)
.unwrap_or(false)
}
fn update(&mut self, new_info: SectionSigned<NodeState>) -> bool {
match self.members.entry(*new_info.value.peer.name()) {
Entry::Vacant(entry) => {
let _ = entry.insert(new_info);
true
}
Entry::Occupied(mut entry) => {
match (entry.get().value.state, new_info.value.state) {
(MembershipState::Joined, MembershipState::Joined)
if new_info.value.peer.age() > entry.get().value.peer.age() => {}
(MembershipState::Joined, MembershipState::Left)
| (MembershipState::Joined, MembershipState::Relocated(_))
| (MembershipState::Relocated(_), MembershipState::Left) => {}
_ => return false,
};
let _ = entry.insert(new_info);
true
}
}
}
fn prune_not_matching(&mut self, prefix: &Prefix) {
self.members = mem::take(&mut self.members)
.into_iter()
.filter(|(name, _)| prefix.matches(name))
.collect();
}
}
pub struct IntoIter(btree_map::IntoIter<XorName, SectionSigned<NodeState>>);
impl Iterator for IntoIter {
type Item = SectionSigned<NodeState>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(_, info)| info)
}
}
fn elder_candidates<'a, I>(
elder_size: usize,
current_elders: &SectionAuthorityProvider,
members: I,
) -> Vec<Peer>
where
I: IntoIterator<Item = &'a SectionSigned<NodeState>>,
{
members
.into_iter()
.sorted_by(|lhs, rhs| cmp_elder_candidates(lhs, rhs, current_elders))
.map(|info| info.value.peer)
.take(elder_size)
.collect()
}
fn cmp_elder_candidates(
lhs: &SectionSigned<NodeState>,
rhs: &SectionSigned<NodeState>,
current_elders: &SectionAuthorityProvider,
) -> Ordering {
cmp_elder_candidates_by_membership_state(&lhs.value.state, &rhs.value.state)
.then_with(|| rhs.value.peer.age().cmp(&lhs.value.peer.age()))
.then_with(|| {
let lhs_is_elder = is_elder(&lhs.value, current_elders);
let rhs_is_elder = is_elder(&rhs.value, current_elders);
match (lhs_is_elder, rhs_is_elder) {
(true, false) => Ordering::Less,
(false, true) => Ordering::Greater,
_ => Ordering::Equal,
}
})
.then_with(|| lhs.signed.signature.cmp(&rhs.signed.signature))
}
fn cmp_elder_candidates_by_membership_state(
lhs: &MembershipState,
rhs: &MembershipState,
) -> Ordering {
use MembershipState::*;
match (lhs, rhs) {
(Joined, Joined) | (Relocated(_), Relocated(_)) => Ordering::Equal,
(Joined, Relocated(_)) | (_, Left) => Ordering::Less,
(Relocated(_), Joined) | (Left, _) => Ordering::Greater,
}
}
fn is_active(info: &NodeState, current_elders: &SectionAuthorityProvider) -> bool {
match info.state {
MembershipState::Joined => true,
MembershipState::Relocated(_) if is_elder(info, current_elders) => true,
_ => false,
}
}
fn is_elder(info: &NodeState, current_elders: &SectionAuthorityProvider) -> bool {
current_elders.contains_elder(info.peer.name())
}