Skip to content

Commit c14939a

Browse files
authored
voting_service: add a refresh queue for standstill messages (anza-xyz#13293)
1 parent c61419a commit c14939a

8 files changed

Lines changed: 454 additions & 103 deletions

File tree

core/src/tvu.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ use {
3535
event::{LatestSwitchRequest, LeaderWindowInfo, VotorEventReceiver, VotorEventSender},
3636
vote_history::VoteHistory,
3737
vote_history_storage::VoteHistoryStorage,
38-
voting_service::{VotingService as BLSVotingService, VotingServiceOverride},
38+
voting_service::{
39+
VOTOR_RATE_LIMIT_PPS, VotingService as BLSVotingService, VotingServiceOverride,
40+
},
3941
votor::{Votor, VotorConfig},
4042
},
4143
agave_votor_messages::{
@@ -311,7 +313,7 @@ impl Tvu {
311313
..Default::default()
312314
};
313315
let qos_config = SimpleQosConfig {
314-
max_streams_per_second: 30,
316+
max_streams_per_second: VOTOR_RATE_LIMIT_PPS,
315317
// Cap by # of active validators (some overhead for epoch boundaries)
316318
max_staked_connections: MAX_ALPENGLOW_VOTE_ACCOUNTS * 2,
317319
// Two staked connection per validator to account for hotspares
@@ -522,7 +524,7 @@ impl Tvu {
522524
cluster_info: cluster_info.clone(),
523525
leader_schedule_cache: leader_schedule_cache.clone(),
524526
consensus_metrics_sender,
525-
highest_finalized,
527+
highest_finalized: highest_finalized.clone(),
526528
bank_forks_controller,
527529
bls_sender: bls_sender.clone(),
528530
commitment_sender: votor_commitment_sender,
@@ -615,6 +617,7 @@ impl Tvu {
615617
vote_history_storage,
616618
bls_connection_cache,
617619
bank_forks.clone(),
620+
highest_finalized,
618621
voting_service_test_override,
619622
);
620623

runtime/src/validated_block_finalization.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,20 @@ impl ValidatedBlockFinalizationCert {
239239
)
240240
}
241241

242+
/// Returns cloned copies of the certificates that prove this finalization.
243+
///
244+
/// For slow finalization this returns the finalize certificate followed by the notarize
245+
/// certificate. For fast finalization this returns the single fast-finalize certificate.
246+
pub fn clone_certificates(&self) -> Vec<Certificate> {
247+
match &self.kind {
248+
ValidatedBlockFinalizationCertKind::Finalize {
249+
finalize_cert,
250+
notarize_cert,
251+
} => vec![finalize_cert.clone(), notarize_cert.clone()],
252+
ValidatedBlockFinalizationCertKind::FastFinalize(cert) => vec![cert.clone()],
253+
}
254+
}
255+
242256
/// Returns the data needed to calculating and paying vote rewards.
243257
pub fn vote_rewards_input(&self) -> (&HashSet<Pubkey>, Slot) {
244258
(&self.signers, self.slot())
@@ -438,11 +452,10 @@ mod tests {
438452
notar_aggregate: None,
439453
};
440454

441-
let result = ValidatedBlockFinalizationCert::try_from_footer(block_final_cert, &bank);
442-
assert!(
443-
result.is_ok(),
444-
"Valid fast finalize certificate should pass verification: {result:?}"
445-
);
455+
let validated =
456+
ValidatedBlockFinalizationCert::try_from_footer(block_final_cert, &bank)
457+
.expect("Valid fast finalize certificate should pass verification");
458+
assert_eq!(validated.clone_certificates(), vec![fast_finalize_cert]);
446459
}
447460

448461
// Test 2: Slow finalize (requires 60% stake = 3300 for both certs)
@@ -475,10 +488,12 @@ mod tests {
475488
notar_aggregate: Some(VotesAggregate::from_certificate(&notarize_cert)),
476489
};
477490

478-
let result = ValidatedBlockFinalizationCert::try_from_footer(block_final_cert, &bank);
479-
assert!(
480-
result.is_ok(),
481-
"Valid slow finalize certificate should pass verification: {result:?}"
491+
let validated =
492+
ValidatedBlockFinalizationCert::try_from_footer(block_final_cert, &bank)
493+
.expect("Valid slow finalize certificate should pass verification");
494+
assert_eq!(
495+
validated.clone_certificates(),
496+
vec![finalize_cert, notarize_cert]
482497
);
483498
}
484499
}

votor-messages/src/certificate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pod_wrapper! {
2626
derive(AbiExample),
2727
frozen_abi(digest = "5WqvPnvSnVXQFrAs9o29szFGDiCk45Pgk8K1evTZSrwo")
2828
)]
29-
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SchemaWrite, SchemaRead)]
29+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, SchemaWrite, SchemaRead)]
3030
pub struct Certificate {
3131
/// The certificate type.
3232
pub cert_type: CertificateType,

votor-messages/src/consensus_message.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub struct Block {
5454
derive(AbiExample),
5555
frozen_abi(digest = "CTiXEk2aQbpf6TS6PNKcaTsGkLruDvAYsTLFhHKW2vsm")
5656
)]
57-
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SchemaWrite, SchemaRead)]
57+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, SchemaWrite, SchemaRead)]
5858
pub struct VoteMessage {
5959
/// The type of the vote.
6060
pub vote: Vote,
@@ -71,7 +71,7 @@ pub struct VoteMessage {
7171
derive(AbiExample, AbiEnumVisitor),
7272
frozen_abi(digest = "CbPatwRWz8NyUAj3HeAxAAWAWTxJnHGAfekLspUQpMHN")
7373
)]
74-
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SchemaWrite, SchemaRead)]
74+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, SchemaWrite, SchemaRead)]
7575
#[allow(clippy::large_enum_variant)]
7676
pub enum ConsensusMessage {
7777
/// A vote from a single party.
@@ -102,6 +102,14 @@ impl ConsensusMessage {
102102
bitmap,
103103
})
104104
}
105+
106+
/// Returns the slot this message is for.
107+
pub fn slot(&self) -> Slot {
108+
match self {
109+
Self::Vote(vote) => vote.vote.slot(),
110+
Self::Certificate(certificate) => certificate.cert_type.slot(),
111+
}
112+
}
105113
}
106114

107115
impl From<Certificate> for ConsensusMessage {

votor/src/consensus_pool.rs

Lines changed: 18 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use {
2828
solana_hash::Hash,
2929
solana_pubkey::Pubkey,
3030
solana_runtime::{bank::Bank, validated_block_finalization::ValidatedBlockFinalizationCert},
31-
std::{cmp::Ordering, collections::BTreeMap, num::NonZero, sync::Arc},
31+
std::{collections::BTreeMap, num::NonZero, sync::Arc},
3232
thiserror::Error,
3333
};
3434

@@ -629,33 +629,15 @@ impl ConsensusPool {
629629
}
630630

631631
pub(crate) fn get_certs_for_standstill(&self) -> Vec<Arc<Certificate>> {
632-
let (highest_slot, has_fast_finalize) = self
632+
let highest_slot = self
633633
.highest_finalized_slot_cert
634634
.as_ref()
635-
.map(|certs| (certs.slot(), certs.is_fast()))
636-
.unwrap_or((0, false));
635+
.map(ValidatedBlockFinalizationCert::slot)
636+
.unwrap_or(0);
637637
self.completed_certificates
638638
.iter()
639639
.filter_map(|(cert_type, cert)| {
640-
let cert_to_send = match (
641-
cert_type.slot().cmp(&highest_slot),
642-
cert_type,
643-
has_fast_finalize,
644-
) {
645-
(Ordering::Greater, _, _)
646-
| (
647-
Ordering::Equal,
648-
CertificateType::Finalize(_) | CertificateType::Notarize(_),
649-
false,
650-
)
651-
| (Ordering::Equal, CertificateType::FinalizeFast(_), true) => {
652-
Some(cert.clone())
653-
}
654-
(Ordering::Equal, CertificateType::FinalizeFast(_), false) => {
655-
panic!("Should not happen while certificate pool is single threaded")
656-
}
657-
_ => None,
658-
};
640+
let cert_to_send = (cert_type.slot() > highest_slot).then(|| cert.clone());
659641
if cert_to_send.is_some() {
660642
trace!(
661643
"{}: Refreshing certificate {:?}",
@@ -1917,13 +1899,10 @@ mod tests {
19171899
bitmap: dummy_bitmap(),
19181900
};
19191901
ctx.add_message(ConsensusMessage::Certificate(cert_5));
1920-
// Should return only FinalizeFast cert on 5
1902+
// Slot 5 is now the highest finalized slot, so standstill cert refresh only returns
1903+
// certificates for later slots.
19211904
let certs = ctx.pool.get_certs_for_standstill();
1922-
assert_eq!(certs.len(), 1);
1923-
assert!(
1924-
certs[0].cert_type.slot() == 5
1925-
&& matches!(certs[0].cert_type, CertificateType::FinalizeFast(_))
1926-
);
1905+
assert!(certs.is_empty());
19271906

19281907
// Now add Notarize cert on 6
19291908
let cert_6 = Certificate {
@@ -1935,11 +1914,9 @@ mod tests {
19351914
bitmap: dummy_bitmap(),
19361915
};
19371916
ctx.add_message(ConsensusMessage::Certificate(cert_6));
1938-
// Should return certs on 5 and 6
1917+
// Should return certs after highest finalized slot 5.
19391918
let certs = ctx.pool.get_certs_for_standstill();
1940-
assert_eq!(certs.len(), 2);
1941-
assert!(certs.iter().any(|cert| cert.cert_type.slot() == 5
1942-
&& matches!(cert.cert_type, CertificateType::FinalizeFast(_))));
1919+
assert_eq!(certs.len(), 1);
19431920
assert!(certs.iter().any(|cert| cert.cert_type.slot() == 6
19441921
&& matches!(cert.cert_type, CertificateType::Notarize(_))));
19451922

@@ -1960,14 +1937,10 @@ mod tests {
19601937
bitmap: dummy_bitmap(),
19611938
};
19621939
ctx.add_message(ConsensusMessage::Certificate(cert_6_notarize_fallback));
1963-
// This should not be returned because 6 is the current highest finalized slot
1964-
// only Notarize/Finalze/FinalizeFast should be returned
1940+
// Slot 6 is now the current highest finalized slot, so no slot-6 certs should
1941+
// be returned by the queued refresh path.
19651942
let certs = ctx.pool.get_certs_for_standstill();
1966-
assert_eq!(certs.len(), 2);
1967-
assert!(certs.iter().any(|cert| cert.cert_type.slot() == 6
1968-
&& matches!(cert.cert_type, CertificateType::Finalize(_))));
1969-
assert!(certs.iter().any(|cert| cert.cert_type.slot() == 6
1970-
&& matches!(cert.cert_type, CertificateType::Notarize(_))));
1943+
assert!(certs.is_empty());
19711944

19721945
// Add another skip on 7
19731946
let cert_7 = Certificate {
@@ -1976,13 +1949,9 @@ mod tests {
19761949
bitmap: dummy_bitmap(),
19771950
};
19781951
ctx.add_message(ConsensusMessage::Certificate(cert_7));
1979-
// Should return certs on 6 and 7
1952+
// Should return certs after highest finalized slot 6.
19801953
let certs = ctx.pool.get_certs_for_standstill();
1981-
assert_eq!(certs.len(), 3);
1982-
assert!(certs.iter().any(|cert| cert.cert_type.slot() == 6
1983-
&& matches!(cert.cert_type, CertificateType::Finalize(_))));
1984-
assert!(certs.iter().any(|cert| cert.cert_type.slot() == 6
1985-
&& matches!(cert.cert_type, CertificateType::Notarize(_))));
1954+
assert_eq!(certs.len(), 1);
19861955
assert!(
19871956
certs.iter().any(|cert| cert.cert_type.slot() == 7
19881957
&& matches!(cert.cert_type, CertificateType::Skip(_)))
@@ -2005,13 +1974,10 @@ mod tests {
20051974
};
20061975
ctx.add_message(ConsensusMessage::Certificate(cert_8_notarize));
20071976

2008-
// Should only return certs on 8 now
1977+
// Slot 8 is now the current highest finalized slot, so latest-finalization
1978+
// certs are refreshed from highest_finalized instead of this queue.
20091979
let certs = ctx.pool.get_certs_for_standstill();
2010-
assert_eq!(certs.len(), 2);
2011-
assert!(certs.iter().any(|cert| cert.cert_type.slot() == 8
2012-
&& matches!(cert.cert_type, CertificateType::Finalize(_))));
2013-
assert!(certs.iter().any(|cert| cert.cert_type.slot() == 8
2014-
&& matches!(cert.cert_type, CertificateType::Notarize(_))));
1980+
assert!(certs.is_empty());
20151981
}
20161982

20171983
#[test]

0 commit comments

Comments
 (0)