use {
crate::block_creation_loop::rewards::msg_types::{
RewardRequest, RewardRespSucc, RewardResponse,
},
agave_bls_sigverify::rewards::rewards_wants_vote,
agave_votor_messages::{
consensus_message::VoteMessage,
reward_certificate::{BuildRewardCertsRespError, NUM_SLOTS_FOR_REWARD},
},
crossbeam_channel::RecvError,
entry::Entry,
solana_clock::Slot,
solana_gossip::cluster_info::ClusterInfo,
solana_ledger::leader_schedule_cache::LeaderScheduleCache,
solana_runtime::bank::Bank,
std::{collections::BTreeMap, sync::Arc},
};
mod entry;
pub(super) struct CertsBuilder {
votes: BTreeMap<Slot, Entry>,
cluster_info: Arc<ClusterInfo>,
leader_schedule: Arc<LeaderScheduleCache>,
}
impl CertsBuilder {
pub(super) fn new(
cluster_info: Arc<ClusterInfo>,
leader_schedule: Arc<LeaderScheduleCache>,
) -> Self {
Self {
votes: BTreeMap::default(),
cluster_info,
leader_schedule,
}
}
fn build_certs(
&mut self,
bank_slot: Slot,
) -> Result<RewardRespSucc, BuildRewardCertsRespError> {
let Some(reward_slot) = bank_slot.checked_sub(NUM_SLOTS_FOR_REWARD) else {
return Ok(RewardRespSucc::default());
};
self.votes = self.votes.split_off(&reward_slot);
match self.votes.remove(&reward_slot) {
None => Ok(RewardRespSucc::default()),
Some(entry) => entry.build_certs(reward_slot),
}
}
pub(super) fn build_request(
&mut self,
request: Result<RewardRequest, RecvError>,
) -> Result<(), ()> {
let my_pubkey = self.cluster_info.id();
match request {
Ok(RewardRequest {
bank_slot,
reply_sender,
}) => {
let resp = RewardResponse {
result: self.build_certs(bank_slot),
};
let _ = reply_sender.send(resp).inspect_err(|_| {
info!(
"{my_pubkey}: channel to send reply for bank_slot={bank_slot} disconnected"
);
});
Ok(())
}
Err(_) => {
error!("{my_pubkey}: build reward certs channel is disconnected; exiting.");
Err(())
}
}
}
fn wants_vote(&self, root_slot: Slot, vote: &VoteMessage) -> bool {
if !rewards_wants_vote(
&self.cluster_info,
&self.leader_schedule,
root_slot,
&vote.vote,
) {
return false;
}
let Some(entry) = self.votes.get(&vote.vote.slot()) else {
return true;
};
entry.wants_vote(vote)
}
pub(super) fn add_vote(&mut self, root_bank: &Bank, vote: &VoteMessage) {
let slot = vote.vote.slot();
let Some(rank_map) = root_bank.get_rank_map(slot) else {
warn!(
"failed to look up rank_map for slot {slot} using bank for slot {}",
root_bank.slot()
);
return;
};
let max_validators = rank_map.len();
let root_slot = root_bank.slot();
self.votes = self
.votes
.split_off(&root_slot.saturating_sub(NUM_SLOTS_FOR_REWARD));
if !self.wants_vote(root_slot, vote) {
return;
}
match self
.votes
.entry(vote.vote.slot())
.or_insert(Entry::new(max_validators))
.add_vote(rank_map, vote)
{
Ok(()) => (),
Err(e) => {
warn!("Adding vote {vote:?} failed with {e}");
}
}
}
}