mod stats;
use self::stats::NetworkStats;
use crate::{
dkg::{verify_signed, SectionSignedUtils, Signed},
peer::PeerUtils,
section::SectionAuthorityProviderUtils,
Error, Result,
};
use secured_linked_list::SecuredLinkedList;
use sn_messaging::{
node::{Network, OtherSection, Peer, PrefixMap, SectionSigned},
SectionAuthorityProvider,
};
use std::iter;
use xor_name::{Prefix, XorName};
pub trait NetworkUtils {
fn new() -> Self;
fn closest(&self, name: &XorName) -> Option<&SectionAuthorityProvider>;
fn all(&self) -> Box<dyn Iterator<Item = &SectionAuthorityProvider> + '_>;
fn get(&self, prefix: &Prefix) -> Option<&SectionAuthorityProvider>;
fn elders(&'_ self) -> Box<dyn Iterator<Item = Peer> + '_>;
fn get_elder(&self, name: &XorName) -> Option<Peer>;
fn merge(&mut self, other: Network, section_chain: &SecuredLinkedList);
fn update_section(
&mut self,
section_auth: SectionSigned<SectionAuthorityProvider>,
key_signed: Option<Signed>,
section_chain: &SecuredLinkedList,
) -> bool;
fn keys(&self) -> Box<dyn Iterator<Item = (Prefix, bls::PublicKey)> + '_>;
fn key_by_name(&self, name: &XorName) -> Result<bls::PublicKey>;
fn key_by_prefix(&self, prefix: &Prefix) -> Option<bls::PublicKey>;
fn section_by_name(&self, name: &XorName) -> Result<SectionAuthorityProvider>;
fn network_stats(&self, our: &SectionAuthorityProvider) -> NetworkStats;
fn network_elder_counts(&self, our: &SectionAuthorityProvider) -> (u64, u64, bool);
}
impl NetworkUtils for Network {
fn new() -> Self {
Self {
sections: PrefixMap::new(),
}
}
fn closest(&self, name: &XorName) -> Option<&SectionAuthorityProvider> {
self.all()
.min_by(|lhs, rhs| lhs.prefix.cmp_distance(&rhs.prefix, name))
}
fn all(&self) -> Box<dyn Iterator<Item = &SectionAuthorityProvider> + '_> {
Box::new(self.sections.iter().map(|info| &info.section_auth.value))
}
fn get(&self, prefix: &Prefix) -> Option<&SectionAuthorityProvider> {
self.sections
.get(prefix)
.map(|info| &info.section_auth.value)
}
fn elders(&'_ self) -> Box<dyn Iterator<Item = Peer> + '_> {
Box::new(self.all().flat_map(|info| info.peers()))
}
fn get_elder(&self, name: &XorName) -> Option<Peer> {
self.sections
.get_matching(name)?
.section_auth
.value
.get_addr(name)
.map(|addr| {
let mut peer = Peer::new(*name, addr);
peer.set_reachable(true);
peer
})
}
fn merge(&mut self, other: Network, section_chain: &SecuredLinkedList) {
for entry in other.sections {
if entry.verify(section_chain) {
let _ = self.sections.insert(entry);
}
}
}
fn update_section(
&mut self,
section_auth: SectionSigned<SectionAuthorityProvider>,
key_signed: Option<Signed>,
section_chain: &SecuredLinkedList,
) -> bool {
let info = OtherSection {
section_auth: section_auth.clone(),
key_signed,
};
if !info.verify(section_chain) {
return false;
}
if let Some(old) = self.sections.insert(info) {
if old.section_auth == section_auth {
return false;
}
}
true
}
fn keys(&self) -> Box<dyn Iterator<Item = (Prefix, bls::PublicKey)> + '_> {
Box::new(self.sections.iter().map(|entry| {
(
entry.section_auth.value.prefix,
entry.section_auth.value.section_key(),
)
}))
}
fn key_by_name(&self, name: &XorName) -> Result<bls::PublicKey> {
self.sections
.get_matching(name)
.ok_or(Error::NoMatchingSection)
.map(|entry| entry.section_auth.value.section_key())
}
fn key_by_prefix(&self, prefix: &Prefix) -> Option<bls::PublicKey> {
self.sections
.get_equal_or_ancestor(prefix)
.map(|entry| entry.section_auth.value.section_key())
}
fn section_by_name(&self, name: &XorName) -> Result<SectionAuthorityProvider> {
self.sections
.get_matching(name)
.ok_or(Error::NoMatchingSection)
.map(|value| value.section_auth.value.clone())
}
fn network_stats(&self, our: &SectionAuthorityProvider) -> NetworkStats {
let (known_elders, total_elders, total_elders_exact) = self.network_elder_counts(our);
NetworkStats {
known_elders,
total_elders,
total_elders_exact,
}
}
fn network_elder_counts(&self, our: &SectionAuthorityProvider) -> (u64, u64, bool) {
let known_prefixes = iter::once(&our.prefix).chain(
self.sections
.iter()
.map(|info| &info.section_auth.value.prefix),
);
let is_exact = Prefix::default().is_covered_by(known_prefixes.clone());
let network_fraction: f64 = known_prefixes
.map(|p| 1.0 / (p.bit_count() as f64).exp2())
.sum();
let known = our.elder_count() + self.elders().count();
let total = known as f64 / network_fraction;
(known as u64, total.ceil() as u64, is_exact)
}
}
pub trait OtherSectionUtils {
fn verify(&self, section_chain: &SecuredLinkedList) -> bool;
}
impl OtherSectionUtils for OtherSection {
fn verify(&self, section_chain: &SecuredLinkedList) -> bool {
if let Some(key_signed) = &self.key_signed {
section_chain.has_key(&key_signed.public_key)
&& verify_signed(key_signed, &self.section_auth.signed.public_key)
&& self.section_auth.self_verify()
} else {
self.section_auth.verify(section_chain)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{dkg, section};
use rand::Rng;
#[test]
fn closest() -> Result<()> {
let sk = bls::SecretKey::random();
let chain = SecuredLinkedList::new(sk.public_key());
let p01: Prefix = "01".parse().unwrap();
let p10: Prefix = "10".parse().unwrap();
let p11: Prefix = "11".parse().unwrap();
let mut map = Network::new();
let _ = map.update_section(gen_section_auth(&sk, p01)?, None, &chain);
let _ = map.update_section(gen_section_auth(&sk, p10)?, None, &chain);
let mut rng = rand::thread_rng();
let n01 = p01.substituted_in(rng.gen());
let n10 = p10.substituted_in(rng.gen());
let n11 = p11.substituted_in(rng.gen());
assert_eq!(map.closest(&n01).map(|i| &i.prefix), Some(&p01));
assert_eq!(map.closest(&n10).map(|i| &i.prefix), Some(&p10));
assert_eq!(map.closest(&n11).map(|i| &i.prefix), Some(&p10));
Ok(())
}
fn gen_section_auth(
sk: &bls::SecretKey,
prefix: Prefix,
) -> Result<SectionSigned<SectionAuthorityProvider>> {
let (section_auth, _, _) = section::test_utils::gen_section_authority_provider(prefix, 5);
dkg::test_utils::section_signed(sk, section_auth)
}
}