use std::collections::HashMap;
use libp2p_identity::PeerId;
use web_time::Instant;
use crate::{peer_score::RejectReason, MessageId, ValidationError};
#[derive(Default)]
pub(crate) struct GossipPromises {
promises: HashMap<MessageId, HashMap<PeerId, Instant>>,
}
impl GossipPromises {
pub(crate) fn contains(&self, message: &MessageId) -> bool {
self.promises.contains_key(message)
}
pub(crate) fn peers_for_message(&self, message_id: &MessageId) -> Vec<PeerId> {
self.promises
.get(message_id)
.map(|peers| peers.keys().copied().collect())
.unwrap_or_default()
}
pub(crate) fn add_promise(&mut self, peer: PeerId, messages: &[MessageId], expires: Instant) {
for message_id in messages {
self.promises
.entry(message_id.clone())
.or_default()
.entry(peer)
.or_insert(expires);
}
}
pub(crate) fn message_delivered(&mut self, message_id: &MessageId) {
self.promises.remove(message_id);
}
pub(crate) fn reject_message(&mut self, message_id: &MessageId, reason: &RejectReason) {
match reason {
RejectReason::ValidationError(ValidationError::InvalidSignature) => (),
RejectReason::SelfOrigin => (),
_ => {
self.promises.remove(message_id);
}
};
}
pub(crate) fn get_broken_promises(&mut self) -> HashMap<PeerId, usize> {
let now = Instant::now();
let mut result = HashMap::new();
self.promises.retain(|msg, peers| {
peers.retain(|peer_id, expires| {
if *expires < now {
let count = result.entry(*peer_id).or_insert(0);
*count += 1;
tracing::debug!(
peer=%peer_id,
message=%msg,
"[Penalty] The peer broke the promise to deliver message in time!"
);
false
} else {
true
}
});
!peers.is_empty()
});
result
}
}