use {
agave_votor_messages::consensus_message::Block,
crossbeam_channel::{Receiver, Sender},
solana_clock::Slot,
solana_runtime::bank::Bank,
std::{
cmp::Ordering,
sync::{Arc, Mutex},
time::Instant,
},
};
#[derive(Debug, Clone)]
pub struct CompletedBlock {
pub slot: Slot,
pub bank: Arc<Bank>,
}
#[derive(Copy, Clone, Debug)]
pub struct LeaderWindowInfo {
pub start_slot: Slot,
pub end_slot: Slot,
pub parent_block: Block,
pub block_timer: Instant,
}
pub type VotorEventSender = Sender<VotorEvent>;
pub type VotorEventReceiver = Receiver<VotorEvent>;
#[derive(Debug, Clone)]
pub enum VotorEvent {
Block(CompletedBlock),
BlockNotarized(Block),
BlockNotarFallback(Block),
FirstShred(Slot),
ParentReady { slot: Slot, parent_block: Block },
TimeoutCrashedLeader(Slot),
Timeout(Slot),
SafeToNotar(Block),
SafeToSkip(Slot),
ProduceWindow(LeaderWindowInfo),
Finalized(Block, bool),
Standstill(Slot),
SetIdentity,
}
impl VotorEvent {
pub(crate) fn should_ignore(&self, root: Slot) -> bool {
match self {
VotorEvent::Block(completed_block) => completed_block.slot <= root,
VotorEvent::Timeout(s)
| VotorEvent::SafeToSkip(s)
| VotorEvent::TimeoutCrashedLeader(s)
| VotorEvent::FirstShred(s)
| VotorEvent::SafeToNotar(Block {
slot: s,
block_id: _,
})
| VotorEvent::Finalized(
Block {
slot: s,
block_id: _,
},
_,
)
| VotorEvent::BlockNotarized(Block {
slot: s,
block_id: _,
})
| VotorEvent::BlockNotarFallback(Block {
slot: s,
block_id: _,
})
| VotorEvent::ParentReady {
slot: s,
parent_block: _,
} => s <= &root,
VotorEvent::Standstill(s) => s < &root,
VotorEvent::ProduceWindow(info) => info.start_slot <= root,
VotorEvent::SetIdentity => false,
}
}
}
pub type RepairEventSender = Sender<RepairEvent>;
pub type RepairEventReceiver = Receiver<RepairEvent>;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum RepairEvent {
FetchBlock { block: Block },
}
impl RepairEvent {
pub fn slot(&self) -> Slot {
match self {
RepairEvent::FetchBlock { block } => block.slot,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum SwitchBankEvent {
Switch { block: Block },
}
impl SwitchBankEvent {
pub fn block(&self) -> Block {
match self {
SwitchBankEvent::Switch { block } => *block,
}
}
}
impl Ord for SwitchBankEvent {
fn cmp(&self, other: &Self) -> Ordering {
let block = self.block();
let other_block = other.block();
match block.slot.cmp(&other_block.slot) {
Ordering::Equal => other_block.block_id.cmp(&block.block_id),
ordering => ordering,
}
}
}
impl PartialOrd for SwitchBankEvent {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(Clone, Default)]
pub struct LatestSwitchRequest(Arc<Mutex<Option<SwitchBankEvent>>>);
impl LatestSwitchRequest {
pub fn try_advance(&self, event: SwitchBankEvent) -> Option<SwitchBankEvent> {
let mut guard = self.0.lock().unwrap();
match guard.as_ref() {
Some(cur) if event <= *cur => None,
_ => guard.replace(event),
}
}
pub fn take(&self) -> Option<SwitchBankEvent> {
self.0.lock().unwrap().take()
}
}
#[cfg(test)]
mod tests {
use {super::*, solana_hash::Hash};
fn block(slot: u64, id_byte: u8) -> Block {
let mut bytes = [0; 32];
bytes[31] = id_byte;
Block {
slot,
block_id: Hash::new_from_array(bytes),
}
}
fn switch(block: Block) -> SwitchBankEvent {
SwitchBankEvent::Switch { block }
}
#[test]
fn latest_switch_request_advances_by_slot_with_lowest_block_id_tie_breaker() {
let latest = LatestSwitchRequest::default();
let slot_3_high = switch(block(3, 9));
let slot_3_low = switch(block(3, 3));
let slot_2 = switch(block(2, 1));
let slot_4 = switch(block(4, 255));
assert_eq!(latest.try_advance(slot_3_high), None);
assert_eq!(latest.try_advance(slot_2), None);
assert_eq!(latest.take(), Some(slot_3_high));
assert_eq!(latest.try_advance(slot_3_high), None);
assert_eq!(latest.try_advance(slot_3_low), Some(slot_3_high));
assert_eq!(latest.try_advance(slot_3_high), None);
assert_eq!(latest.try_advance(slot_4), Some(slot_3_low));
assert_eq!(latest.take(), Some(slot_4));
}
}