Skip to content

Commit 3dbee46

Browse files
carllinbw-solana
authored andcommitted
Parse out alpenglow votes in vote_parser.rs (anza-xyz#95)
1 parent db2d1a5 commit 3dbee46

File tree

6 files changed

+241
-51
lines changed

6 files changed

+241
-51
lines changed

core/src/cluster_info_vote_listener.rs

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,7 @@ use {
3939
timing::AtomicInterval,
4040
transaction::Transaction,
4141
},
42-
solana_vote::{
43-
vote_parser::{self, ParsedVote},
44-
vote_transaction::VoteTransaction,
45-
},
42+
solana_vote::vote_parser::{self, ParsedVote, ParsedVoteTransaction},
4643
std::{
4744
cmp::max,
4845
collections::HashMap,
@@ -287,6 +284,10 @@ impl ClusterInfoVoteListener {
287284
votes.len(),
288285
);
289286
let root_bank = root_bank_cache.root_bank();
287+
let first_alpenglow_slot = root_bank
288+
.feature_set
289+
.activated_slot(&solana_feature_set::secp256k1_program_enabled::id())
290+
.unwrap_or(Slot::MAX);
290291
let epoch_schedule = root_bank.epoch_schedule();
291292
votes
292293
.into_iter()
@@ -297,8 +298,12 @@ impl ClusterInfoVoteListener {
297298
!packet_batch[0].meta().discard()
298299
})
299300
.filter_map(|(tx, packet_batch)| {
300-
let (vote_account_key, vote, ..) = vote_parser::parse_vote_transaction(&tx)?;
301+
let (vote_account_key, vote, ..) = vote_parser::parse_vote_transaction(&tx)
302+
.or_else(|| vote_parser::parse_alpenglow_vote_transaction(&tx))?;
301303
let slot = vote.last_voted_slot()?;
304+
if (slot >= first_alpenglow_slot) ^ vote.is_alpenglow_vote() {
305+
return None;
306+
}
302307
let epoch = epoch_schedule.get_epoch(slot);
303308
let authorized_voter = root_bank
304309
.epoch_stakes(epoch)?
@@ -394,6 +399,7 @@ impl ClusterInfoVoteListener {
394399
subscriptions: &RpcSubscriptions,
395400
gossip_verified_vote_hash_sender: &GossipVerifiedVoteHashSender,
396401
verified_vote_sender: &VerifiedVoteSender,
402+
// TODO: send replayed Alpenglow transactions as well
397403
replay_votes_receiver: &ReplayVoteReceiver,
398404
bank_notification_sender: &Option<BankNotificationSender>,
399405
duplicate_confirmed_slot_sender: &Option<DuplicateConfirmedSlotsSender>,
@@ -442,7 +448,7 @@ impl ClusterInfoVoteListener {
442448

443449
#[allow(clippy::too_many_arguments)]
444450
fn track_new_votes_and_notify_confirmations(
445-
vote: VoteTransaction,
451+
vote: ParsedVoteTransaction,
446452
vote_pubkey: &Pubkey,
447453
vote_transaction_signature: Signature,
448454
vote_tracker: &VoteTracker,
@@ -459,13 +465,17 @@ impl ClusterInfoVoteListener {
459465
bank_hash_cache: &mut BankHashCache,
460466
dumped_slot_subscription: &Mutex<bool>,
461467
) {
462-
if vote.is_empty() {
468+
if vote.slots().is_empty() {
463469
return;
464470
}
465471

466472
// Hold lock for whole function to ensure hash consistency with bank_forks
467473
let mut slots_dumped = dumped_slot_subscription.lock().unwrap();
468-
let (last_vote_slot, last_vote_hash) = vote.last_voted_slot_hash().unwrap();
474+
let Some((last_vote_slot, last_vote_hash)) = vote.last_voted_slot_hash() else {
475+
// TODO: handle sending skip votes to replay because skp votes don't have a hash and will
476+
// return
477+
return;
478+
};
469479

470480
let latest_vote_slot = latest_vote_slot_per_validator
471481
.entry(*vote_pubkey)
@@ -535,20 +545,29 @@ impl ClusterInfoVoteListener {
535545
let _ = gossip_verified_vote_hash_sender.send((*vote_pubkey, slot, hash));
536546
}
537547

538-
if reached_threshold_results[0] {
539-
if let Some(sender) = duplicate_confirmed_slot_sender {
540-
let _ = sender.send(vec![(slot, hash)]);
548+
let first_alpenglow_slot = root_bank
549+
.feature_set
550+
.activated_slot(&solana_feature_set::secp256k1_program_enabled::id())
551+
.unwrap_or(Slot::MAX);
552+
553+
// Optimistic confirmation and duplicate confirmation are no longer relevant after
554+
// alpenglow
555+
if slot < first_alpenglow_slot {
556+
if reached_threshold_results[0] {
557+
if let Some(sender) = duplicate_confirmed_slot_sender {
558+
let _ = sender.send(vec![(slot, hash)]);
559+
}
541560
}
542-
}
543-
if reached_threshold_results[1] {
544-
new_optimistic_confirmed_slots.push((slot, hash));
545-
// Notify subscribers about new optimistic confirmation
546-
if let Some(sender) = bank_notification_sender {
547-
sender
548-
.send(BankNotification::OptimisticallyConfirmed(slot))
549-
.unwrap_or_else(|err| {
550-
warn!("bank_notification_sender failed: {:?}", err)
551-
});
561+
if reached_threshold_results[1] {
562+
new_optimistic_confirmed_slots.push((slot, hash));
563+
// Notify subscribers about new optimistic confirmation
564+
if let Some(sender) = bank_notification_sender {
565+
sender
566+
.send(BankNotification::OptimisticallyConfirmed(slot))
567+
.unwrap_or_else(|err| {
568+
warn!("bank_notification_sender failed: {:?}", err)
569+
});
570+
}
552571
}
553572
}
554573

@@ -588,7 +607,10 @@ impl ClusterInfoVoteListener {
588607
*latest_vote_slot = max(*latest_vote_slot, last_vote_slot);
589608

590609
if is_new_vote {
591-
subscriptions.notify_vote(*vote_pubkey, vote, vote_transaction_signature);
610+
// TODO: Make sure to notify on notarization votes as well
611+
if let Some(tower_vote) = vote.try_into_tower_transaction() {
612+
subscriptions.notify_vote(*vote_pubkey, tower_vote, vote_transaction_signature);
613+
}
592614
let _ = verified_vote_sender.send((*vote_pubkey, vote_slots));
593615
}
594616
}
@@ -616,7 +638,10 @@ impl ClusterInfoVoteListener {
616638
let mut gossip_vote_txn_processing_time = Measure::start("gossip_vote_processing_time");
617639
let votes = gossip_vote_txs
618640
.iter()
619-
.filter_map(vote_parser::parse_vote_transaction)
641+
.filter_map(|tx| {
642+
vote_parser::parse_vote_transaction(tx)
643+
.or_else(|| vote_parser::parse_alpenglow_vote_transaction(tx))
644+
})
620645
.zip(repeat(/*is_gossip:*/ true))
621646
.chain(replayed_votes.into_iter().zip(repeat(/*is_gossip:*/ false)));
622647
for ((vote_pubkey, vote, _switch_proof, signature), is_gossip) in votes {
@@ -637,7 +662,7 @@ impl ClusterInfoVoteListener {
637662
latest_vote_slot_per_validator,
638663
bank_hash_cache,
639664
dumped_slot_subscription,
640-
);
665+
)
641666
}
642667
gossip_vote_txn_processing_time.stop();
643668
let gossip_vote_txn_processing_time_us = gossip_vote_txn_processing_time.as_us();
@@ -749,7 +774,7 @@ mod tests {
749774
pubkey::Pubkey,
750775
signature::{Keypair, Signature, Signer},
751776
},
752-
solana_vote::vote_transaction,
777+
solana_vote::vote_transaction::{self, VoteTransaction},
753778
solana_vote_program::vote_state::{TowerSync, Vote, MAX_LOCKOUT_HISTORY},
754779
std::{
755780
collections::BTreeSet,
@@ -962,7 +987,7 @@ mod tests {
962987
replay_votes_sender
963988
.send((
964989
vote_keypair.pubkey(),
965-
VoteTransaction::from(replay_vote.clone()),
990+
ParsedVoteTransaction::Tower(VoteTransaction::from(replay_vote.clone())),
966991
switch_proof_hash,
967992
Signature::default(),
968993
))
@@ -1285,7 +1310,10 @@ mod tests {
12851310
replay_votes_sender
12861311
.send((
12871312
vote_keypair.pubkey(),
1288-
VoteTransaction::from(Vote::new(vec![vote_slot], Hash::default())),
1313+
ParsedVoteTransaction::Tower(VoteTransaction::from(Vote::new(
1314+
vec![vote_slot],
1315+
Hash::default(),
1316+
))),
12891317
switch_proof_hash,
12901318
Signature::default(),
12911319
))
@@ -1334,6 +1362,7 @@ mod tests {
13341362
run_test_process_votes3(Some(Hash::default()));
13351363
}
13361364

1365+
// TODO: Add Alpenglow equivalent tests
13371366
#[test]
13381367
fn test_vote_tracker_references() {
13391368
// Create some voters at genesis
@@ -1388,7 +1417,10 @@ mod tests {
13881417
// Add gossip vote for same slot, should not affect outcome
13891418
vec![(
13901419
validator0_keypairs.vote_keypair.pubkey(),
1391-
VoteTransaction::from(Vote::new(vec![voted_slot], Hash::default())),
1420+
ParsedVoteTransaction::Tower(VoteTransaction::from(Vote::new(
1421+
vec![voted_slot],
1422+
Hash::default(),
1423+
))),
13921424
None,
13931425
Signature::default(),
13941426
)],
@@ -1437,7 +1469,10 @@ mod tests {
14371469
vote_txs,
14381470
vec![(
14391471
validator_keypairs[1].vote_keypair.pubkey(),
1440-
VoteTransaction::from(Vote::new(vec![first_slot_in_new_epoch], Hash::default())),
1472+
ParsedVoteTransaction::Tower(VoteTransaction::from(Vote::new(
1473+
vec![first_slot_in_new_epoch],
1474+
Hash::default(),
1475+
))),
14411476
None,
14421477
Signature::default(),
14431478
)],

core/src/voting_service.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ impl VotingService {
198198
tx,
199199
last_voted_slot,
200200
} => {
201-
cluster_info.refresh_vote(tx, last_voted_slot);
201+
cluster_info.refresh_vote(tx, last_voted_slot, false);
202202
}
203203
}
204204
}

gossip/src/cluster_info.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -784,11 +784,15 @@ impl ClusterInfo {
784784
Ok(())
785785
}
786786

787-
pub fn push_vote_at_index(&self, vote: Transaction, vote_index: u8) {
787+
pub fn push_vote_at_index(&self, vote: Transaction, vote_index: u8, is_alpenglow: bool) {
788788
assert!(vote_index < MAX_VOTES);
789789
let self_pubkey = self.id();
790790
let now = timestamp();
791-
let vote = Vote::new(self_pubkey, vote, now).unwrap();
791+
let vote = if is_alpenglow {
792+
Vote::new_alpenglow(self_pubkey, vote, now).unwrap()
793+
} else {
794+
Vote::new(self_pubkey, vote, now).unwrap()
795+
};
792796
let vote = CrdsData::Vote(vote_index, vote);
793797
let vote = CrdsValue::new(vote, &self.keypair());
794798
let mut gossip_crds = self.gossip.crds.write().unwrap();
@@ -873,7 +877,7 @@ impl ClusterInfo {
873877

874878
pub fn push_alpenglow_vote(&self, vote: Transaction) {
875879
let vote_index = self.find_alpenglow_vote_index_to_evict();
876-
self.push_vote_at_index(vote, vote_index);
880+
self.push_vote_at_index(vote, vote_index, true);
877881
}
878882

879883
pub fn push_vote(&self, tower: &[Slot], vote: Transaction) {
@@ -894,10 +898,15 @@ impl ClusterInfo {
894898
);
895899
};
896900
debug_assert!(vote_index < MAX_VOTES);
897-
self.push_vote_at_index(vote, vote_index);
901+
self.push_vote_at_index(vote, vote_index, false);
898902
}
899903

900-
pub fn refresh_vote(&self, refresh_vote: Transaction, refresh_vote_slot: Slot) {
904+
pub fn refresh_vote(
905+
&self,
906+
refresh_vote: Transaction,
907+
refresh_vote_slot: Slot,
908+
is_alpenglow: bool,
909+
) {
901910
let vote_index = {
902911
let self_pubkey = self.id();
903912
let gossip_crds =
@@ -923,7 +932,7 @@ impl ClusterInfo {
923932
// We don't write to an arbitrary index, because it may replace one of this validator's
924933
// existing votes on the network.
925934
if let Some(vote_index) = vote_index {
926-
self.push_vote_at_index(refresh_vote, vote_index);
935+
self.push_vote_at_index(refresh_vote, vote_index, is_alpenglow);
927936
} else {
928937
// If you don't see a vote with the same slot yet, this means you probably
929938
// restarted, and need to repush and evict the oldest vote
@@ -935,7 +944,7 @@ impl ClusterInfo {
935944
return;
936945
};
937946
debug_assert!(vote_index < MAX_VOTES);
938-
self.push_vote_at_index(refresh_vote, vote_index);
947+
self.push_vote_at_index(refresh_vote, vote_index, is_alpenglow);
939948
}
940949
}
941950

@@ -3534,7 +3543,7 @@ mod tests {
35343543
&[refresh_ix], // instructions
35353544
None, // payer
35363545
);
3537-
cluster_info.refresh_vote(refresh_tx.clone(), refresh_slot);
3546+
cluster_info.refresh_vote(refresh_tx.clone(), refresh_slot, false);
35383547
let current_votes = cluster_info.get_votes(&mut Cursor::default());
35393548
assert_eq!(initial_votes, current_votes);
35403549
assert!(!current_votes.contains(&refresh_tx));
@@ -3551,7 +3560,7 @@ mod tests {
35513560
&[refresh_ix], // instructions
35523561
None, // payer
35533562
);
3554-
cluster_info.refresh_vote(refresh_tx.clone(), refresh_slot);
3563+
cluster_info.refresh_vote(refresh_tx.clone(), refresh_slot, false);
35553564

35563565
// This should evict the latest vote since it's for a slot less than refresh_slot
35573566
let votes = cluster_info.get_votes(&mut Cursor::default());
@@ -3600,7 +3609,7 @@ mod tests {
36003609

36013610
// Trying to refresh vote when it doesn't yet exist in gossip
36023611
// should add the vote without eviction if there is room in the gossip table.
3603-
cluster_info.refresh_vote(refresh_tx.clone(), refresh_slot);
3612+
cluster_info.refresh_vote(refresh_tx.clone(), refresh_slot, false);
36043613

36053614
// Should be two votes in gossip
36063615
cursor = Cursor::default();
@@ -3625,7 +3634,7 @@ mod tests {
36253634
&[&new_signer],
36263635
latest_refreshed_recent_blockhash,
36273636
);
3628-
cluster_info.refresh_vote(latest_refresh_tx.clone(), refresh_slot);
3637+
cluster_info.refresh_vote(latest_refresh_tx.clone(), refresh_slot, false);
36293638
// Sleep to avoid votes with same timestamp causing later vote to not override prior vote
36303639
std::thread::sleep(Duration::from_millis(1));
36313640
}

gossip/src/crds_data.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,15 @@ impl Vote {
364364
})
365365
}
366366

367+
pub fn new_alpenglow(from: Pubkey, transaction: Transaction, wallclock: u64) -> Option<Self> {
368+
vote_parser::parse_alpenglow_vote_transaction(&transaction).map(|(_, vote, ..)| Self {
369+
from,
370+
transaction,
371+
wallclock,
372+
slot: vote.last_voted_slot(),
373+
})
374+
}
375+
367376
/// New random Vote for tests and benchmarks.
368377
fn new_rand<R: Rng>(rng: &mut R, pubkey: Option<Pubkey>) -> Self {
369378
Self {
@@ -398,8 +407,12 @@ impl<'de> Deserialize<'de> for Vote {
398407
vote.transaction
399408
.sanitize()
400409
.map_err(serde::de::Error::custom)?;
401-
Self::new(vote.from, vote.transaction, vote.wallclock)
402-
.ok_or_else(|| serde::de::Error::custom("invalid vote tx"))
410+
let vote = if vote_parser::is_alpenglow_vote_transaction(&vote.transaction) {
411+
Self::new_alpenglow(vote.from, vote.transaction, vote.wallclock)
412+
} else {
413+
Self::new(vote.from, vote.transaction, vote.wallclock)
414+
};
415+
vote.ok_or_else(|| serde::de::Error::custom("invalid vote tx"))
403416
}
404417
}
405418

local-cluster/tests/local_cluster.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2815,6 +2815,8 @@ fn test_oc_bad_signatures() {
28152815
|(_label, leader_vote_tx)| {
28162816
let vote = vote_parser::parse_vote_transaction(&leader_vote_tx)
28172817
.map(|(_, vote, ..)| vote)
2818+
.unwrap()
2819+
.try_into_tower_transaction()
28182820
.unwrap();
28192821
// Filter out empty votes
28202822
if !vote.is_empty() {
@@ -4010,6 +4012,8 @@ fn run_duplicate_shreds_broadcast_leader(vote_on_duplicate: bool) {
40104012
if label.pubkey() == bad_leader_id {
40114013
let vote = vote_parser::parse_vote_transaction(&leader_vote_tx)
40124014
.map(|(_, vote, ..)| vote)
4015+
.unwrap()
4016+
.try_into_tower_transaction()
40134017
.unwrap();
40144018
// Filter out empty votes
40154019
if !vote.is_empty() {
@@ -4073,7 +4077,7 @@ fn run_duplicate_shreds_broadcast_leader(vote_on_duplicate: bool) {
40734077
);
40744078
gossip_vote_index += 1;
40754079
gossip_vote_index %= MAX_VOTES;
4076-
cluster_info.push_vote_at_index(vote_tx, gossip_vote_index);
4080+
cluster_info.push_vote_at_index(vote_tx, gossip_vote_index, false);
40774081
}
40784082
}
40794083
},

0 commit comments

Comments
 (0)