Skip to content
1 change: 1 addition & 0 deletions programs/sbf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions svm/examples/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 52 additions & 2 deletions votor/src/certificate_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use {
certificate_limits_and_vote_types,
certificate_pool::{
parent_ready_tracker::ParentReadyTracker,
stats::CertificatePoolStats,
vote_certificate::{CertificateError, VoteCertificate},
vote_pool::{
DuplicateBlockVotePool, SimpleVotePool, VotePool, VotePoolType, VotedBlockKey,
Expand Down Expand Up @@ -37,6 +38,7 @@ use {
};

pub mod parent_ready_tracker;
mod stats;
mod vote_certificate;
mod vote_pool;

Expand Down Expand Up @@ -108,6 +110,8 @@ pub struct CertificatePool {
root_epoch: Epoch,
/// The certificate sender, if set, newly created certificates will be sent here
certificate_sender: Option<Sender<(CertificateId, CertificateMessage)>>,
/// Stats for the certificate pool
stats: CertificatePoolStats,
}

impl CertificatePool {
Expand Down Expand Up @@ -136,6 +140,7 @@ impl CertificatePool {
root_epoch: Epoch::default(),
certificate_sender,
parent_ready_tracker,
stats: CertificatePoolStats::new(),
};

// Update the epoch_stakes_map and root
Expand Down Expand Up @@ -245,6 +250,8 @@ impl CertificatePool {
});
let new_cert = Arc::new(vote_certificate.certificate());
self.send_and_insert_certificate(cert_id, new_cert.clone(), events)?;
self.stats
.incr_cert_type(new_cert.certificate.certificate_type, true);
new_certificates_to_send.push(new_cert);
}
Ok(new_certificates_to_send)
Expand Down Expand Up @@ -387,6 +394,8 @@ impl CertificatePool {
message: &BLSMessage,
events: &mut Vec<VotorEvent>,
) -> Result<(Option<Slot>, Vec<Arc<CertificateMessage>>), AddVoteError> {
// We add stats reporting here because we should almost always have a message.
self.stats.maybe_report();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking this might fit better higher up the stack (like in certificate_pool_ingest main loop). If we move it up there, we could even make this a sub-component of the CertificatePoolServiceStats reporting

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I don't think the two stats should be mixed together, they each maintain the status of the corresponding data structure.

But I can move the trigger there. Done.

let current_highest_finalized_slot = self.highest_finalized_slot;
let new_certficates_to_send = match message {
BLSMessage::Vote(vote_message) => {
Expand All @@ -408,7 +417,6 @@ impl CertificatePool {
fn add_vote(
&mut self,
my_vote_pubkey: &Pubkey,

vote_message: &VoteMessage,
events: &mut Vec<VotorEvent>,
) -> Result<Vec<Arc<CertificateMessage>>, AddVoteError> {
Expand All @@ -424,11 +432,14 @@ impl CertificatePool {
"Validator stake is zero for pubkey: {validator_vote_key}"
);

self.stats.incoming_votes = self.stats.incoming_votes.saturating_add(1);
if slot < self.root {
self.stats.out_of_range_votes = self.stats.out_of_range_votes.saturating_add(1);
return Err(AddVoteError::UnrootedSlot);
}
// We only allow votes
if slot > self.root.saturating_add(MAX_SLOT_AGE) {
self.stats.out_of_range_votes = self.stats.out_of_range_votes.saturating_add(1);
return Err(AddVoteError::SlotInFuture);
}

Expand All @@ -447,6 +458,7 @@ impl CertificatePool {
if let Some(conflicting_type) =
self.has_conflicting_vote(slot, vote_type, &validator_vote_key, &voted_block_key)
{
self.stats.conflicting_votes = self.stats.conflicting_votes.saturating_add(1);
return Err(AddVoteError::ConflictingVoteType(
vote_type,
conflicting_type,
Expand All @@ -462,6 +474,7 @@ impl CertificatePool {
&validator_vote_key,
validator_stake,
) {
self.stats.exist_votes = self.stats.exist_votes.saturating_add(1);
return Ok(vec![]);
}
// Check if this new vote generated a safe to notar or safe to skip
Expand All @@ -470,11 +483,15 @@ impl CertificatePool {
// everytime.
if self.safe_to_skip(my_vote_pubkey, slot) {
events.push(VotorEvent::SafeToSkip(slot));
self.stats.event_safe_to_skip = self.stats.event_safe_to_skip.saturating_add(1);
}
for (block_id, bank_hash) in self.safe_to_notar(my_vote_pubkey, slot) {
events.push(VotorEvent::SafeToNotar((slot, block_id, bank_hash)));
self.stats.event_safe_to_notarize = self.stats.event_safe_to_notarize.saturating_add(1);
}

self.stats.incr_ingested_vote_type(vote_type);

self.update_certificates(vote, voted_block_key, events, total_stake)
}

Expand All @@ -485,11 +502,21 @@ impl CertificatePool {
) -> Result<Vec<Arc<CertificateMessage>>, AddVoteError> {
let certificate = &certificate_message.certificate;
let certificate_id = CertificateId::from(certificate);
self.stats.incoming_certs = self.stats.incoming_certs.saturating_add(1);
if certificate.slot < self.root {
self.stats.out_of_range_certs = self.stats.out_of_range_certs.saturating_add(1);
return Err(AddVoteError::UnrootedSlot);
}
if self.completed_certificates.contains_key(&certificate_id) {
self.stats.exist_certs = self.stats.exist_certs.saturating_add(1);
return Ok(vec![]);
}
let new_certificate = Arc::new(certificate_message.clone());
self.send_and_insert_certificate(certificate_id, new_certificate.clone(), events)?;

self.stats
.incr_cert_type(certificate.certificate_type, false);

Ok(vec![new_certificate])
}

Expand Down Expand Up @@ -1724,7 +1751,7 @@ mod tests {
}

#[test]
fn test_reject_conflicting_votes() {
fn test_handle_new_root() {
let validator_keypairs = (0..10)
.map(|_| ValidatorVoteKeypairs::new_rand())
.collect::<Vec<_>>();
Expand All @@ -1740,5 +1767,28 @@ mod tests {
let new_bank = Arc::new(create_bank(3, new_bank, &Pubkey::new_unique()));
pool.handle_new_root(new_bank);
assert_eq!(pool.root(), 3);
// Send a vote on slot 1, it should be rejected
let vote = Vote::new_skip_vote(1);
assert!(pool
.add_message(
&Pubkey::new_unique(),
&dummy_transaction(&validator_keypairs, &vote, 0),
&mut vec![]
)
.is_err());
// Send a cert on slot 2, it should be rejected
let cert = BLSMessage::Certificate(CertificateMessage {
certificate: Certificate {
slot: 2,
certificate_type: CertificateType::Notarize,
block_id: Some(Hash::new_unique()),
replayed_bank_hash: Some(Hash::new_unique()),
},
signature: BLSSignature::default(),
bitmap: BitVec::new(),
});
assert!(pool
.add_message(&Pubkey::new_unique(), &cert, &mut vec![])
.is_err());
}
}
Loading