use sn_data_types::{Error, Money, Result, RewardCounter};
pub struct RewardCounterSet {
quorum: usize,
counters: Vec<RewardCounter>,
}
impl RewardCounterSet {
pub fn new(expected_counters: usize, counters: Vec<RewardCounter>) -> Result<Self> {
if expected_counters % 2 == 0 || 3 > expected_counters {
return Err(Error::InvalidOperation);
}
let quorum = (expected_counters / 3) * 2;
Ok(Self { quorum, counters })
}
pub fn len(&self) -> usize {
self.counters.len()
}
pub fn is_empty(&self) -> bool {
self.counters.is_empty()
}
pub fn add(&mut self, counter: RewardCounter) {
self.counters.push(counter)
}
pub fn agreed_value(&self) -> Option<RewardCounter> {
let count = self.counters.len();
if self.quorum > count {
return None;
}
let median_reward = self.median_reward();
let median_work = self.median_work();
Some(RewardCounter {
reward: median_reward,
work: median_work,
})
}
fn median_reward(&self) -> Money {
let mut rewards: Vec<Money> = self
.counters
.clone()
.into_iter()
.map(|c| c.reward)
.collect();
rewards.sort();
if rewards.len() % 2 == 0 {
let mid_0 = rewards.len() / 2;
let mid_1 = (rewards.len() / 2) + 1;
let mid_0 = rewards.clone().remove(mid_0).as_nano();
let mid_1 = rewards.remove(mid_1).as_nano();
Money::from_nano((mid_0 + mid_1) / 2)
} else {
let mid = rewards.len() / 2;
rewards.remove(mid)
}
}
fn median_work(&self) -> u64 {
let mut works: Vec<u64> = self.counters.clone().into_iter().map(|c| c.work).collect();
works.sort();
if works.len() % 2 == 0 {
let mid_0 = works.len() / 2;
let mid_1 = (works.len() / 2) + 1;
let mid_0 = works.clone().remove(mid_0);
let mid_1 = works.remove(mid_1);
(mid_0 + mid_1) / 2
} else {
let mid = works.len() / 2;
works.remove(mid)
}
}
}