use crate::{
kd_tree::{KdTree, Key},
manager::Manager,
peer::{Peer, TransactionRating},
EigenError,
};
use ark_std::{
collections::{BTreeMap, BTreeSet},
fmt::Debug,
marker::PhantomData,
vec::Vec,
Zero,
};
use rand::prelude::RngCore;
pub trait NetworkConfig: Debug {
const DELTA: f64;
const SIZE: usize;
const MAX_ITERATIONS: usize;
const PRE_TRUST_WEIGHT: f64;
const MANAGER_PER_PEER: usize;
}
#[derive(Debug)]
pub struct Network<C: NetworkConfig> {
peers: BTreeMap<Key, Peer>,
managers: BTreeMap<Key, Manager>,
is_converged: bool,
_config: PhantomData<C>,
}
impl<C: NetworkConfig> Network<C> {
pub fn bootstrap(
pre_trust_scores: Vec<f64>,
) -> Result<Self, EigenError> {
if pre_trust_scores.len() != C::SIZE {
return Err(EigenError::InvalidPreTrustScores);
}
if C::MANAGER_PER_PEER > (C::SIZE - 1) {
return Err(EigenError::InvalidManagerPerPeer);
}
let pre_trust_score_map: BTreeMap<Key, f64> = pre_trust_scores
.into_iter()
.enumerate()
.map(|(i, score)| (Key::from(i), score))
.collect();
let mut peers = BTreeMap::new();
let mut managers = BTreeMap::new();
let keys: Vec<Key> = (0..C::SIZE).map(|x| Key::from(x)).collect();
for key in &keys {
let new_peer = Peer::new(*key, pre_trust_score_map.clone());
peers.insert(*key, new_peer);
}
for key in &keys {
let new_manager = Manager::new(*key, pre_trust_score_map.clone());
managers.insert(*key, new_manager);
}
Self::connect_peers_and_managers(&keys, &mut peers, &mut managers)?;
Ok(Self {
peers,
managers,
is_converged: false,
_config: PhantomData,
})
}
pub fn mock_transaction(
&mut self,
i: usize,
j: usize,
rating: TransactionRating,
) -> Result<(), EigenError> {
let peer_i_index = Key::from(i);
let peer = self
.peers
.get_mut(&peer_i_index)
.ok_or(EigenError::PeerNotFound)?;
let peer_j_index = Key::from(j);
peer.mock_rate_transaction(&peer_j_index, rating);
Ok(())
}
pub fn converge<R: RngCore>(&mut self, _rng: &mut R) -> Result<(), EigenError> {
self.reset();
let mut temp_managers = self.managers.clone();
for _ in 0..C::MAX_ITERATIONS {
let mut is_everyone_converged = true;
for (_, manager) in temp_managers.iter_mut() {
manager.heartbeat(&self.peers, &self.managers, C::DELTA, C::PRE_TRUST_WEIGHT)?;
is_everyone_converged = is_everyone_converged && manager.is_converged();
}
if is_everyone_converged {
self.is_converged = true;
break;
}
}
self.managers = temp_managers;
Ok(())
}
pub fn reset(&mut self) {
self.is_converged = false;
for (_, manager) in self.managers.iter_mut() {
manager.reset();
}
}
pub fn get_global_trust_scores(&self) -> Result<Vec<f64>, EigenError> {
let mut cached_global_scores: BTreeMap<Key, f64> = BTreeMap::new();
for (peer_index, peer) in self.peers.iter() {
let global_score = Manager::calculate_global_trust_score_for(peer, &self.managers)?;
cached_global_scores.insert(*peer_index, global_score);
}
let mut sum = f64::zero();
for (peer_index, _) in self.peers.iter() {
let score = cached_global_scores
.get(peer_index)
.ok_or(EigenError::PeerNotFound)?;
sum += score;
}
let mut ti_vec = Vec::new();
for (peer_index, _) in self.peers.iter() {
let cached_score = cached_global_scores
.get(peer_index)
.ok_or(EigenError::PeerNotFound)?;
ti_vec.push(cached_score / sum);
}
Ok(ti_vec)
}
pub fn find_managers_for_peer(
index: &Key,
manager_tree: &KdTree,
) -> Result<Vec<Key>, EigenError> {
let mut hash = *index;
let mut manager_keys: BTreeSet<Key> = BTreeSet::new();
let mut all_keys: BTreeSet<Key> = BTreeSet::new();
while all_keys.len() < manager_tree.size() {
hash = hash.hash();
let manager_key = manager_tree
.search(hash)
.map_err(|_| EigenError::PeerNotFound)?;
all_keys.insert(manager_key);
if manager_keys.contains(&manager_key) || manager_key == *index {
continue;
}
manager_keys.insert(manager_key);
if manager_keys.len() == C::MANAGER_PER_PEER {
let managers_vec = manager_keys.into_iter().collect();
return Ok(managers_vec);
}
}
return Err(EigenError::FailedToFindManagers);
}
pub fn connect_peers_and_managers(
keys: &Vec<Key>,
peers: &mut BTreeMap<Key, Peer>,
managers: &mut BTreeMap<Key, Manager>,
) -> Result<(), EigenError> {
let manager_tree = KdTree::new(keys.clone()).map_err(|_| EigenError::InvalidManagerKeys)?;
for key in keys {
let managers_vec = Self::find_managers_for_peer(key, &manager_tree)?;
let peer = peers.get_mut(key).ok_or(EigenError::PeerNotFound)?;
peer.set_managers(managers_vec.clone());
for manager_key in managers_vec {
let manager = managers
.get_mut(&manager_key)
.ok_or(EigenError::PeerNotFound)?;
manager.add_child(*key);
}
}
Ok(())
}
pub fn is_converged(&self) -> bool {
self.is_converged
}
}
#[cfg(test)]
mod test {
use super::*;
use ark_std::One;
use rand::thread_rng;
#[derive(Debug)]
struct TestNetworkConfig;
impl NetworkConfig for TestNetworkConfig {
const DELTA: f64 = 0.001;
const MANAGER_PER_PEER: usize = 1;
const MAX_ITERATIONS: usize = 1000;
const PRE_TRUST_WEIGHT: f64 = 0.5;
const SIZE: usize = 2;
}
#[test]
fn bootstrapping() {
let num_peers: usize = TestNetworkConfig::SIZE;
let mut pre_trust_scores = vec![0.0; num_peers];
pre_trust_scores[0] = 0.5;
pre_trust_scores[1] = 0.5;
let network = Network::<TestNetworkConfig>::bootstrap(pre_trust_scores).unwrap();
assert_eq!(network.peers.len(), num_peers);
}
#[test]
fn fail_to_bootstrap_with_invalid_managers_per_peer() {
#[derive(Debug)]
struct InvalidNetworkConfig;
impl NetworkConfig for InvalidNetworkConfig {
const DELTA: f64 = 0.001;
const MANAGER_PER_PEER: usize = 2;
const MAX_ITERATIONS: usize = 1000;
const PRE_TRUST_WEIGHT: f64 = 0.5;
const SIZE: usize = 2;
}
let num_peers: usize = InvalidNetworkConfig::SIZE;
let mut pre_trust_scores = vec![0.0; num_peers];
pre_trust_scores[0] = 0.5;
pre_trust_scores[1] = 0.5;
let network = Network::<InvalidNetworkConfig>::bootstrap(pre_trust_scores.clone());
assert_eq!(network.unwrap_err(), EigenError::InvalidManagerPerPeer);
let key0 = Key::from(0);
let key1 = Key::from(1);
let keys = vec![key0, key1];
let tree = KdTree::new(keys).unwrap();
let res = Network::<InvalidNetworkConfig>::find_managers_for_peer(&key0, &tree);
assert_eq!(res.unwrap_err(), EigenError::FailedToFindManagers);
}
#[test]
fn invalid_mock_transaction() {
let num_peers: usize = TestNetworkConfig::SIZE;
let pre_trust_scores = vec![0.0; num_peers];
let mut network = Network::<TestNetworkConfig>::bootstrap(pre_trust_scores).unwrap();
let res = network.mock_transaction(4, 1, TransactionRating::Positive);
assert_eq!(res.unwrap_err(), EigenError::PeerNotFound);
}
#[test]
fn invalid_pretrust_scores() {
let num_peers: usize = TestNetworkConfig::SIZE - 1;
let pre_trust_scores = vec![0.0; num_peers];
let network = Network::<TestNetworkConfig>::bootstrap(pre_trust_scores);
assert_eq!(network.unwrap_err(), EigenError::InvalidPreTrustScores);
}
#[test]
fn gts_is_nan_without_pre_trusted_peers() {
let rng = &mut thread_rng();
let num_peers: usize = TestNetworkConfig::SIZE;
let pre_trust_scores = vec![0.0; num_peers];
let mut network = Network::<TestNetworkConfig>::bootstrap(pre_trust_scores).unwrap();
network
.mock_transaction(0, 1, TransactionRating::Positive)
.unwrap();
network
.mock_transaction(1, 0, TransactionRating::Positive)
.unwrap();
network
.mock_transaction(1, 2, TransactionRating::Positive)
.unwrap();
network.converge(rng).unwrap();
assert!(network.is_converged());
let key0 = Key::from(0);
let key1 = Key::from(1);
let peer0_trust_score = network.managers[&key0].get_global_trust_score_for(&key0);
let peer1_trust_score = network.managers[&key0].get_global_trust_score_for(&key1);
assert_eq!(peer0_trust_score, 0.0);
assert_eq!(peer1_trust_score, 0.0);
let global_trust_scores = network.get_global_trust_scores().unwrap();
assert!(global_trust_scores[0].is_nan());
assert!(global_trust_scores[1].is_nan());
}
#[test]
fn converging_with_pre_trusted_peers() {
let rng = &mut thread_rng();
let num_peers: usize = TestNetworkConfig::SIZE;
let default_score = 1. / (num_peers as f64);
let pre_trust_scores = vec![default_score; num_peers];
let mut network = Network::<TestNetworkConfig>::bootstrap(pre_trust_scores).unwrap();
network
.mock_transaction(0, 1, TransactionRating::Positive)
.unwrap();
network
.mock_transaction(1, 0, TransactionRating::Positive)
.unwrap();
network
.mock_transaction(1, 2, TransactionRating::Positive)
.unwrap();
let key0 = Key::from(0);
let key1 = Key::from(1);
let peer0_global_score =
Manager::calculate_global_trust_score_for(&network.peers[&key0], &network.managers)
.unwrap();
let sum_of_local_scores_0 =
network.peers[&key1].get_local_trust_score(&key0) * peer0_global_score;
assert_eq!(sum_of_local_scores_0, 0.25);
assert_eq!(peer0_global_score, 0.5);
let new_global_trust_score_0 = (f64::one() - TestNetworkConfig::PRE_TRUST_WEIGHT)
* sum_of_local_scores_0
+ TestNetworkConfig::PRE_TRUST_WEIGHT * network.peers[&key0].get_pre_trust_score();
let peer1_global_score =
Manager::calculate_global_trust_score_for(&network.peers[&key0], &network.managers)
.unwrap();
let sum_of_local_scores_1 =
network.peers[&key0].get_local_trust_score(&key1) * peer1_global_score;
assert_eq!(sum_of_local_scores_1, 0.5);
let new_global_trust_score_1 = (f64::one() - TestNetworkConfig::PRE_TRUST_WEIGHT)
* sum_of_local_scores_1
+ TestNetworkConfig::PRE_TRUST_WEIGHT * network.peers[&key1].get_pre_trust_score();
network.converge(rng).unwrap();
let peer0_score =
Manager::calculate_global_trust_score_for(&network.peers[&key0], &network.managers)
.unwrap();
assert_eq!(peer0_score, new_global_trust_score_0);
assert_eq!(peer0_score, 0.375);
let peer1_score =
Manager::calculate_global_trust_score_for(&network.peers[&key1], &network.managers)
.unwrap();
assert_eq!(peer1_score, new_global_trust_score_1);
assert_eq!(peer1_score, 0.5);
let global_trust_scores = network.get_global_trust_scores().unwrap();
assert_eq!(global_trust_scores[0], 0.42857142857142855);
assert_eq!(global_trust_scores[1], 0.5714285714285714);
}
}