use chrono::{DateTime, Utc};
use exonum::{blockchain::ValidatorKeys, crypto::PublicKey};
use exonum_derive::*;
use exonum_merkledb::{
access::{Access, FromAccess, RawAccessMut},
ProofEntry, ProofMapIndex,
};
#[derive(Debug, FromAccess, RequireArtifact)]
pub struct TimeSchema<T: Access> {
pub validators_times: ProofMapIndex<T::Base, PublicKey, DateTime<Utc>>,
pub time: ProofEntry<T::Base, DateTime<Utc>>,
}
impl<T: Access> TimeSchema<T> {
pub(crate) fn new(access: T) -> Self {
Self::from_root(access).unwrap()
}
}
impl<T: Access> TimeSchema<T>
where
T::Base: RawAccessMut,
{
pub(crate) fn update_validator_time(
&mut self,
author: PublicKey,
time: DateTime<Utc>,
) -> Result<(), ()> {
match self.validators_times.get(&author) {
Some(val_time) if val_time >= time => Err(()),
_ => {
self.validators_times.put(&author, time);
Ok(())
}
}
}
pub(crate) fn update_consolidated_time(&mut self, validator_keys: &[ValidatorKeys]) {
let validator_times = {
let mut times = self
.validators_times
.iter()
.filter_map(|(public_key, time)| {
validator_keys.iter().find_map(|validator| {
if validator.service_key == public_key {
Some(time)
} else {
None
}
})
})
.collect::<Vec<_>>();
times.sort_by(|a, b| b.cmp(a));
times
};
let max_byzantine_nodes = (validator_keys.len() - 1) / 3;
if validator_times.len() <= 2 * max_byzantine_nodes {
return;
}
match self.time.get() {
Some(current_time) if current_time >= validator_times[max_byzantine_nodes] => {}
_ => {
self.time.set(validator_times[max_byzantine_nodes]);
}
}
}
}