use crate::{kd_tree::Key, peer::Peer, EigenError};
use ark_std::{collections::BTreeMap, fmt::Debug, vec::Vec, One, Zero};
#[derive(Clone, Debug)]
pub struct Manager {
index: Key,
global_trust_scores: BTreeMap<Key, f64>,
pre_trust_scores: BTreeMap<Key, f64>,
children_states: BTreeMap<Key, bool>,
children: Vec<Key>,
}
impl Manager {
pub fn new(index: Key, pre_trust_scores: BTreeMap<Key, f64>) -> Self {
Self {
index,
global_trust_scores: pre_trust_scores.clone(),
pre_trust_scores,
children_states: BTreeMap::new(),
children: Vec::new(),
}
}
pub fn add_child(&mut self, child: Key) {
self.children.push(child);
}
pub fn heartbeat(
&mut self,
peers: &BTreeMap<Key, Peer>,
managers: &BTreeMap<Key, Manager>,
delta: f64,
pre_trust_weight: f64,
) -> Result<(), EigenError> {
let children = self.children.clone();
for peer in children {
self.heartbeat_child(&peer, peers, managers, delta, pre_trust_weight)?;
}
Ok(())
}
pub fn heartbeat_child(
&mut self,
index: &Key,
peers: &BTreeMap<Key, Peer>,
managers: &BTreeMap<Key, Manager>,
delta: f64,
pre_trust_weight: f64,
) -> Result<(), EigenError> {
let child_converged = self.children_states.get(index).unwrap_or(&false);
if *child_converged {
return Ok(());
}
let mut cached_global_scores: BTreeMap<Key, f64> = BTreeMap::new();
for (peer_index, peer) in peers.iter() {
let global_score = Self::calculate_global_trust_score_for(peer, managers)?;
cached_global_scores.insert(*peer_index, global_score);
}
let mut new_global_trust_score = f64::zero();
for (key_j, neighbor_j) in peers.iter() {
if index == key_j {
continue;
}
let trust_score = neighbor_j.get_local_trust_score(index);
let global_score = cached_global_scores
.get(key_j)
.ok_or(EigenError::PeerNotFound)?;
let neighbor_opinion = trust_score * global_score;
new_global_trust_score += neighbor_opinion;
}
let peer_d = peers.get(index).ok_or(EigenError::PeerNotFound)?;
new_global_trust_score = (f64::one() - pre_trust_weight) * new_global_trust_score
+ pre_trust_weight * peer_d.get_pre_trust_score();
let diff = (new_global_trust_score - self.get_global_trust_score_for(index)).abs();
if diff <= delta {
self.children_states.insert(*index, true);
}
self.global_trust_scores
.insert(*index, new_global_trust_score);
Ok(())
}
pub fn get_children(&self) -> Vec<Key> {
self.children.clone()
}
pub fn is_converged(&self) -> bool {
for child in self.children.iter() {
if !self.children_states.get(child).unwrap_or(&false) {
return false;
}
}
true
}
pub fn reset(&mut self) {
self.children_states.clear();
}
pub fn get_global_trust_score_for(&self, index: &Key) -> f64 {
*self.global_trust_scores.get(index).unwrap_or(&0.)
}
pub fn get_pre_trust_score(&self) -> f64 {
*self.pre_trust_scores.get(&self.index).unwrap_or(&0.)
}
pub fn get_index(&self) -> Key {
self.index.clone()
}
pub fn calculate_global_trust_score_for(
peer: &Peer,
managers: &BTreeMap<Key, Manager>,
) -> Result<f64, EigenError> {
let mut scores: BTreeMap<[u8; 8], usize> = BTreeMap::new();
let managers_for_peer = peer.get_managers();
let majority = (managers_for_peer.len() * 2) / 3;
for manager_key in managers_for_peer {
let manager = managers.get(&manager_key).ok_or(EigenError::PeerNotFound)?;
let score = manager.get_global_trust_score_for(&peer.get_index());
let score_bytes = score.to_be_bytes();
let count = scores.entry(score_bytes).or_insert(0);
*count += 1;
if *count > majority {
return Ok(score);
}
}
Err(EigenError::GlobalTrustCalculationFailed)
}
}
#[cfg(test)]
mod test {
use super::*;
const DELTA: f64 = 0.00001;
const PRE_TRUST_WEIGHT: f64 = 0.4;
#[test]
fn create_and_add_children() {
let key0 = Key::from(0);
let key1 = Key::from(1);
let mut pre_trust_scores = BTreeMap::new();
pre_trust_scores.insert(key0, 0.4);
pre_trust_scores.insert(key1, 0.4);
let peer0 = Peer::new(key0, pre_trust_scores.clone());
let peer1 = Peer::new(key1, pre_trust_scores.clone());
let mut peers = BTreeMap::new();
peers.insert(key0, peer0);
peers.insert(key1, peer1);
let mut manager = Manager::new(key0, pre_trust_scores.clone());
manager.add_child(key1);
assert_eq!(manager.get_index(), key0);
assert_eq!(manager.get_pre_trust_score(), 0.4);
assert_eq!(manager.get_global_trust_score_for(&key1), 0.4);
assert_eq!(manager.get_children(), vec![key1]);
assert_eq!(manager.is_converged(), false);
}
#[test]
fn vote_on_global_trust_score() {
let key0 = Key::from(0);
let key1 = Key::from(1);
let key2 = Key::from(2);
let mut peer0 = Peer::new(key0, BTreeMap::new());
peer0.set_managers(vec![key1, key2]);
let mut pre_trust_scores = BTreeMap::new();
pre_trust_scores.insert(key0, 0.4);
let mut other_pre_trust_scores = BTreeMap::new();
other_pre_trust_scores.insert(key0, 0.3);
let manager1 = Manager::new(key1, pre_trust_scores.clone());
let manager2 = Manager::new(key2, pre_trust_scores.clone());
let mut managers = BTreeMap::new();
managers.insert(key1, manager1.clone());
managers.insert(key2, manager2.clone());
let res = Manager::calculate_global_trust_score_for(&peer0, &managers).unwrap();
assert_eq!(res, 0.4);
let manager1 = Manager::new(key1, pre_trust_scores.clone());
let manager2 = Manager::new(key2, other_pre_trust_scores.clone());
let mut managers = BTreeMap::new();
managers.insert(key1, manager1.clone());
managers.insert(key2, manager2.clone());
let res = Manager::calculate_global_trust_score_for(&peer0, &managers);
assert_eq!(res.unwrap_err(), EigenError::GlobalTrustCalculationFailed);
}
#[test]
fn should_converge() {
let key0 = Key::from(0);
let key1 = Key::from(1);
let mut pre_trust_scores = BTreeMap::new();
pre_trust_scores.insert(key0, 0.4);
pre_trust_scores.insert(key1, 0.4);
let mut peer0 = Peer::new(key0, pre_trust_scores.clone());
let mut peer1 = Peer::new(key1, pre_trust_scores.clone());
peer0.set_managers(vec![key1]);
peer1.set_managers(vec![key0]);
let mut peers = BTreeMap::new();
peers.insert(key0, peer0);
peers.insert(key1, peer1);
let mut manager0 = Manager::new(key0, pre_trust_scores.clone());
let mut manager1 = Manager::new(key1, pre_trust_scores.clone());
manager0.add_child(key1);
manager1.add_child(key0);
let mut managers = BTreeMap::new();
managers.insert(key0, manager0.clone());
managers.insert(key1, manager1.clone());
while !manager0.is_converged() {
manager0
.heartbeat(&peers, &managers, DELTA, PRE_TRUST_WEIGHT)
.unwrap();
}
assert_eq!(manager0.is_converged(), true);
let global_trust_score_before = manager0.get_global_trust_score_for(&key1);
manager0
.heartbeat(&peers, &managers, DELTA, PRE_TRUST_WEIGHT)
.unwrap();
let global_trust_score_after = manager0.get_global_trust_score_for(&key1);
assert_eq!(global_trust_score_before, global_trust_score_after);
manager0.reset();
assert_eq!(manager0.is_converged(), false);
}
#[test]
fn global_trust_score_deterministic_calculation() {
let key0 = Key::from(0);
let key1 = Key::from(1);
let mut pre_trust_scores = BTreeMap::new();
pre_trust_scores.insert(key0, 0.4);
pre_trust_scores.insert(key1, 0.4);
let mut peer0 = Peer::new(key0, pre_trust_scores.clone());
let mut peer1 = Peer::new(key1, pre_trust_scores.clone());
peer0.set_managers(vec![key1]);
peer1.set_managers(vec![key0]);
let mut peers = BTreeMap::new();
peers.insert(key0, peer0.clone());
peers.insert(key1, peer1);
let mut manager0 = Manager::new(key0, pre_trust_scores.clone());
let mut manager1 = Manager::new(key1, pre_trust_scores.clone());
manager0.add_child(key1);
manager1.add_child(key0);
let mut managers = BTreeMap::new();
managers.insert(key0, manager0.clone());
managers.insert(key1, manager1.clone());
let managers_clone = managers.clone();
for (_, manager) in managers.iter_mut() {
manager
.heartbeat(&peers, &managers_clone, DELTA, PRE_TRUST_WEIGHT)
.unwrap();
}
let sum_of_local_scores =
peers[&key1].get_local_trust_score(&key0) * Manager::calculate_global_trust_score_for(&peer0, &managers_clone).unwrap();
assert_eq!(peers[&key1].get_local_trust_score(&key0), 0.4);
assert_eq!(sum_of_local_scores, 0.16000000000000003);
let new_global_trust_score = (f64::one() - PRE_TRUST_WEIGHT) * sum_of_local_scores
+ PRE_TRUST_WEIGHT * peers[&key0].get_pre_trust_score();
assert_eq!(
managers[&key1].get_global_trust_score_for(&key0),
new_global_trust_score
);
assert_eq!(
managers[&key1].get_global_trust_score_for(&key0),
0.25600000000000006
);
}
}