use crate::{
error::{Error, Result},
majority,
network::Network,
peer::Peer,
section::Section,
ELDER_SIZE,
};
use itertools::Itertools;
use sn_messaging::DstLocation;
use std::{cmp, iter};
use xor_name::XorName;
pub(crate) fn delivery_targets(
dst: &DstLocation,
our_name: &XorName,
section: &Section,
network: &Network,
) -> Result<(Vec<Peer>, usize)> {
if !section.is_elder(our_name) {
let targets: Vec<_> = section.elders_info().peers().copied().collect();
let dg_size = targets.len();
return Ok((targets, dg_size));
}
let (best_section, dg_size) = match dst {
DstLocation::Section(target_name) => {
section_candidates(target_name, our_name, section, network)?
}
DstLocation::EndUser(user) => {
let target_name = user.name();
section_candidates(&target_name, our_name, section, network)?
}
DstLocation::Node(target_name) => {
if target_name == our_name {
return Ok((Vec::new(), 0));
}
if let Some(node) = get_peer(target_name, section, network) {
return Ok((vec![*node], 1));
}
candidates(target_name, our_name, section, network)?
}
DstLocation::Direct => return Err(Error::CannotRoute),
};
Ok((best_section, dg_size))
}
fn section_candidates(
target_name: &XorName,
our_name: &XorName,
section: &Section,
network: &Network,
) -> Result<(Vec<Peer>, usize)> {
let info = iter::once(section.elders_info())
.chain(network.all())
.min_by(|lhs, rhs| lhs.prefix.cmp_distance(&rhs.prefix, target_name))
.unwrap_or_else(|| section.elders_info());
if info.prefix == *section.prefix() || info.prefix.is_neighbour(section.prefix()) {
let section: Vec<_> = info
.peers()
.filter(|node| node.name() != our_name)
.copied()
.collect();
let dg_size = section.len();
return Ok((section, dg_size));
}
candidates(target_name, our_name, section, network)
}
fn candidates(
target_name: &XorName,
our_name: &XorName,
section: &Section,
network: &Network,
) -> Result<(Vec<Peer>, usize)> {
let sections = iter::once(section.elders_info())
.chain(network.all())
.sorted_by(|lhs, rhs| lhs.prefix.cmp_distance(&rhs.prefix, target_name))
.map(|info| (&info.prefix, info.elders.len(), info.elders.values()));
let mut dg_size = majority(ELDER_SIZE);
let mut nodes_to_send = Vec::new();
for (idx, (prefix, len, connected)) in sections.enumerate() {
nodes_to_send.extend(connected.cloned());
dg_size = cmp::min(len, dg_size);
if len < majority(ELDER_SIZE) {
warn!(
"Delivery group only {:?} when it should be {:?}",
len,
majority(ELDER_SIZE)
)
}
if prefix == section.prefix() {
nodes_to_send.retain(|node| node.name() != our_name);
dg_size = nodes_to_send.len();
break;
}
if idx == 0 && nodes_to_send.len() >= dg_size {
break;
}
}
nodes_to_send.sort_by(|lhs, rhs| target_name.cmp_distance(lhs.name(), rhs.name()));
if dg_size > 0 && nodes_to_send.len() >= dg_size {
Ok((nodes_to_send, dg_size))
} else {
Err(Error::CannotRoute)
}
}
fn get_peer<'a>(name: &XorName, section: &'a Section, network: &'a Network) -> Option<&'a Peer> {
section
.members()
.get(name)
.map(|info| &info.peer)
.or_else(|| network.get_elder(name))
}
pub fn signature_targets<I>(dst: &DstLocation, our_elders: I) -> Vec<Peer>
where
I: IntoIterator<Item = Peer>,
{
let dst_name = match dst {
DstLocation::Node(name) => *name,
DstLocation::Section(name) => *name,
DstLocation::EndUser(_) | DstLocation::Direct => {
error!("Invalid destination for signature targets: {:?}", dst);
return vec![];
}
};
let mut list: Vec<_> = our_elders
.into_iter()
.sorted_by(|lhs, rhs| dst_name.cmp_distance(lhs.name(), rhs.name()))
.collect();
list.truncate(cmp::min(list.len(), majority(ELDER_SIZE)));
list
}