use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
use node_data::StepName;
use node_data::bls::PublicKeyBytes;
use node_data::ledger::{Attestation, IterationInfo, StepVotes};
use node_data::message::payload::{RatificationResult, Vote};
use tokio::sync::Mutex;
use tracing::{debug, warn};
#[derive(Clone)]
struct AttestationInfo {
att: Attestation,
quorum_reached_validation: bool,
quorum_reached_ratification: bool,
}
impl fmt::Display for AttestationInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"att_info: {:?}, validation: (step_votes:{:?},quorum_reached:{:?}), ratification: (step_votes:{:?},quorum_reached:{:?})",
self.att.result,
self.att.validation,
self.quorum_reached_validation,
self.att.ratification,
self.quorum_reached_ratification
)
}
}
impl AttestationInfo {
pub(crate) fn new(vote: Vote) -> Self {
AttestationInfo {
att: Attestation {
result: vote.into(),
..Default::default()
},
quorum_reached_validation: false,
quorum_reached_ratification: false,
}
}
pub(crate) fn set_step_votes(
&mut self,
iter: u8,
step_votes: StepVotes,
step: StepName,
quorum_reached: bool,
) {
match step {
StepName::Validation => {
self.att.validation = step_votes;
if quorum_reached {
self.quorum_reached_validation = quorum_reached;
}
}
StepName::Ratification => {
self.att.ratification = step_votes;
if quorum_reached {
self.quorum_reached_ratification = quorum_reached;
}
}
_ => {
warn!(
event = "Invalid step for StepVotes",
iter,
?step,
data = format!("{}", self),
);
return;
}
}
debug!(
event = "StepVotes updated",
iter,
?step,
data = format!("{}", self),
);
}
fn is_ready(&self) -> bool {
match self.att.result {
RatificationResult::Fail(Vote::NoQuorum) => {
self.quorum_reached_ratification
}
RatificationResult::Fail(Vote::Invalid(_)) => {
self.quorum_reached_validation
&& self.quorum_reached_ratification
}
RatificationResult::Fail(Vote::NoCandidate) => {
self.quorum_reached_validation
&& self.quorum_reached_ratification
}
RatificationResult::Success(Vote::Valid(_)) => {
self.quorum_reached_validation
&& self.quorum_reached_ratification
}
_ => false,
}
}
}
pub type SafeAttestationInfoRegistry = Arc<Mutex<AttInfoRegistry>>;
#[derive(Clone)]
struct IterationAtts {
votes: HashMap<Vote, AttestationInfo>,
generator: PublicKeyBytes,
}
impl IterationAtts {
fn new(generator: PublicKeyBytes) -> Self {
Self {
votes: HashMap::new(),
generator,
}
}
fn failed(&self) -> Option<&AttestationInfo> {
self.votes
.values()
.find(|c| c.is_ready() && c.att.result.failed())
}
fn get_or_insert(&mut self, vote: &Vote) -> &mut AttestationInfo {
if !self.votes.contains_key(vote) {
self.votes.insert(*vote, AttestationInfo::new(*vote));
}
self.votes.get_mut(vote).expect("Vote to be inserted")
}
}
pub struct AttInfoRegistry {
att_list: HashMap<u8, IterationAtts>,
}
impl AttInfoRegistry {
pub(crate) fn new() -> Self {
Self {
att_list: HashMap::new(),
}
}
pub(crate) fn set_step_votes(
&mut self,
iteration: u8,
vote: &Vote,
step_votes: StepVotes,
step: StepName,
quorum_reached: bool,
generator: &PublicKeyBytes,
) -> Option<Attestation> {
if step_votes == StepVotes::default() {
return None;
}
let iter_atts = self.get_iteration_atts(iteration, generator);
let att_info = iter_atts.get_or_insert(vote);
att_info.set_step_votes(iteration, step_votes, step, quorum_reached);
if att_info.is_ready() {
return Some(att_info.att);
}
None
}
fn get_iteration_atts(
&mut self,
iteration: u8,
generator: &PublicKeyBytes,
) -> &mut IterationAtts {
self.att_list
.entry(iteration)
.or_insert_with(|| IterationAtts::new(*generator))
}
pub(crate) fn set_attestation(
&mut self,
iteration: u8,
attestation: Attestation,
generator: &PublicKeyBytes,
) {
let iter_atts = self.get_iteration_atts(iteration, generator);
let vote = attestation.result.vote();
let att_info = iter_atts.get_or_insert(vote);
let validation_quorum = !matches!(vote, Vote::NoQuorum);
att_info.set_step_votes(
iteration,
attestation.validation,
StepName::Validation,
validation_quorum,
);
att_info.set_step_votes(
iteration,
attestation.ratification,
StepName::Ratification,
true,
);
}
pub(crate) fn get_failed_atts(&self, to: u8) -> Vec<Option<IterationInfo>> {
let mut res = Vec::with_capacity(to as usize);
for iteration in 0u8..to {
res.push(
self.att_list
.get(&iteration)
.and_then(|iter| {
iter.failed().map(|ci| (ci, iter.generator))
})
.filter(|(ci, _)| ci.is_ready())
.map(|(ci, pk)| (ci.att, pk)),
);
}
res
}
pub(crate) fn get_fail_att(&self, iteration: u8) -> Option<Attestation> {
self.att_list
.get(&iteration)
.and_then(|atts| atts.failed())
.filter(|info| info.is_ready())
.map(|info| info.att)
}
}