Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion programs/vote/benches/process_vote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn create_accounts() -> (Slot, SlotHashes, Vec<TransactionAccount>, Vec<AccountM
);

for next_vote_slot in 0..num_initial_votes {
vote_state.process_next_vote_slot(next_vote_slot, 0, 0);
vote_state.process_next_vote_slot(next_vote_slot, 0, 0, true, true);
}
let mut vote_account_data: Vec<u8> = vec![0; VoteState::size_of()];
let versioned = VoteStateVersions::new_current(vote_state);
Expand Down
81 changes: 62 additions & 19 deletions programs/vote/src/vote_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,9 @@ pub fn process_new_vote_state(
let timely_vote_credits = feature_set.map_or(false, |f| {
f.is_active(&feature_set::timely_vote_credits::id())
});
let deprecate_unused_legacy_vote_plumbing = feature_set.map_or(false, |f| {
f.is_active(&feature_set::deprecate_unused_legacy_vote_plumbing::id())
});
let mut earned_credits = if timely_vote_credits { 0_u64 } else { 1_u64 };

if let Some(new_root) = new_root {
Expand All @@ -621,7 +624,11 @@ pub fn process_new_vote_state(
if current_vote.slot() <= new_root {
if timely_vote_credits || (current_vote.slot() != new_root) {
earned_credits = earned_credits
.checked_add(vote_state.credits_for_vote_at_index(current_vote_state_index))
.checked_add(vote_state.credits_for_vote_at_index(
current_vote_state_index,
timely_vote_credits,
deprecate_unused_legacy_vote_plumbing,
))
.expect("`earned_credits` does not overflow");
}
current_vote_state_index = current_vote_state_index
Expand Down Expand Up @@ -734,11 +741,19 @@ pub fn process_vote_unfiltered(
slot_hashes: &[SlotHash],
epoch: Epoch,
current_slot: Slot,
timely_vote_credits: bool,
deprecate_unused_legacy_vote_plumbing: bool,
) -> Result<(), VoteError> {
check_slots_are_valid(vote_state, vote_slots, &vote.hash, slot_hashes)?;
vote_slots
.iter()
.for_each(|s| vote_state.process_next_vote_slot(*s, epoch, current_slot));
vote_slots.iter().for_each(|s| {
vote_state.process_next_vote_slot(
*s,
epoch,
current_slot,
timely_vote_credits,
deprecate_unused_legacy_vote_plumbing,
)
});
Ok(())
}

Expand All @@ -748,6 +763,8 @@ pub fn process_vote(
slot_hashes: &[SlotHash],
epoch: Epoch,
current_slot: Slot,
timely_vote_credits: bool,
deprecate_unused_legacy_vote_plumbing: bool,
) -> Result<(), VoteError> {
if vote.slots.is_empty() {
return Err(VoteError::EmptySlots);
Expand All @@ -769,6 +786,8 @@ pub fn process_vote(
slot_hashes,
epoch,
current_slot,
timely_vote_credits,
deprecate_unused_legacy_vote_plumbing,
)
}

Expand All @@ -785,6 +804,8 @@ pub fn process_vote_unchecked(vote_state: &mut VoteState, vote: Vote) -> Result<
&slot_hashes,
vote_state.current_epoch(),
0,
true,
true,
)
}

Expand Down Expand Up @@ -1067,7 +1088,18 @@ pub fn process_vote_with_account<S: std::hash::BuildHasher>(
) -> Result<(), InstructionError> {
let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;

process_vote(&mut vote_state, vote, slot_hashes, clock.epoch, clock.slot)?;
let timely_vote_credits = feature_set.is_active(&feature_set::timely_vote_credits::id());
let deprecate_unused_legacy_vote_plumbing =
feature_set.is_active(&feature_set::deprecate_unused_legacy_vote_plumbing::id());
process_vote(
&mut vote_state,
vote,
slot_hashes,
clock.epoch,
clock.slot,
timely_vote_credits,
deprecate_unused_legacy_vote_plumbing,
)?;
if let Some(timestamp) = vote.timestamp {
vote.slots
.iter()
Expand Down Expand Up @@ -1250,7 +1282,7 @@ mod tests {
134, 135,
]
.into_iter()
.for_each(|v| vote_state.process_next_vote_slot(v, 4, 0));
.for_each(|v| vote_state.process_next_vote_slot(v, 4, 0, false, true));

let version1_14_11_serialized = bincode::serialize(&VoteStateVersions::V1_14_11(Box::new(
VoteState1_14_11::from(vote_state.clone()),
Expand Down Expand Up @@ -1732,11 +1764,11 @@ mod tests {
let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();

assert_eq!(
process_vote(&mut vote_state_a, &vote, &slot_hashes, 0, 0),
process_vote(&mut vote_state_a, &vote, &slot_hashes, 0, 0, true, true),
Ok(())
);
assert_eq!(
process_vote(&mut vote_state_b, &vote, &slot_hashes, 0, 0),
process_vote(&mut vote_state_b, &vote, &slot_hashes, 0, 0, true, true),
Ok(())
);
assert_eq!(recent_votes(&vote_state_a), recent_votes(&vote_state_b));
Expand All @@ -1749,12 +1781,12 @@ mod tests {
let vote = Vote::new(vec![0], Hash::default());
let slot_hashes: Vec<_> = vec![(0, vote.hash)];
assert_eq!(
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true, true),
Ok(())
);
let recent = recent_votes(&vote_state);
assert_eq!(
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true, true),
Err(VoteError::VoteTooOld)
);
assert_eq!(recent, recent_votes(&vote_state));
Expand Down Expand Up @@ -1814,7 +1846,7 @@ mod tests {
let vote = Vote::new(vec![0], Hash::default());
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
assert_eq!(
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true, true),
Ok(())
);
assert_eq!(
Expand All @@ -1830,7 +1862,7 @@ mod tests {
let vote = Vote::new(vec![0], Hash::default());
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
assert_eq!(
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true, true),
Ok(())
);

Expand All @@ -1849,7 +1881,7 @@ mod tests {
let vote = Vote::new(vec![0], Hash::default());
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
assert_eq!(
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true, true),
Ok(())
);

Expand All @@ -1866,7 +1898,7 @@ mod tests {

let vote = Vote::new(vec![], Hash::default());
assert_eq!(
process_vote(&mut vote_state, &vote, &[], 0, 0),
process_vote(&mut vote_state, &vote, &[], 0, 0, true, true),
Err(VoteError::EmptySlots)
);
}
Expand Down Expand Up @@ -2163,7 +2195,9 @@ mod tests {
&vote,
&slot_hashes,
0,
vote_group.1 // vote_group.1 is the slot in which the vote was cast
vote_group.1, // vote_group.1 is the slot in which the vote was cast
true,
true
),
Ok(())
);
Expand Down Expand Up @@ -3055,7 +3089,7 @@ mod tests {
// error with `VotesTooOldAllFiltered`
let slot_hashes = vec![(3, Hash::new_unique()), (2, Hash::new_unique())];
assert_eq!(
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0),
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true, true),
Err(VoteError::VotesTooOldAllFiltered)
);

Expand All @@ -3069,7 +3103,7 @@ mod tests {
.1;

let vote = Vote::new(vec![old_vote_slot, vote_slot], vote_slot_hash);
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0).unwrap();
process_vote(&mut vote_state, &vote, &slot_hashes, 0, 0, true, true).unwrap();
assert_eq!(
vote_state
.votes
Expand Down Expand Up @@ -3098,8 +3132,17 @@ mod tests {
.unwrap()
.1;
let vote = Vote::new(vote_slots, vote_hash);
process_vote_unfiltered(&mut vote_state, &vote.slots, &vote, slot_hashes, 0, 0)
.unwrap();
process_vote_unfiltered(
&mut vote_state,
&vote.slots,
&vote,
slot_hashes,
0,
0,
true,
true,
)
.unwrap();
}

vote_state
Expand Down
23 changes: 19 additions & 4 deletions sdk/program/src/vote/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,8 @@ impl VoteState {
next_vote_slot: Slot,
epoch: Epoch,
current_slot: Slot,
timely_vote_credits: bool,
deprecate_unused_legacy_vote_plumbing: bool,
) {
// Ignore votes for slots earlier than we already have votes for
if self
Expand All @@ -526,13 +528,21 @@ impl VoteState {
self.pop_expired_votes(next_vote_slot);

let landed_vote = LandedVote {
latency: Self::compute_vote_latency(next_vote_slot, current_slot),
latency: if timely_vote_credits || !deprecate_unused_legacy_vote_plumbing {
Self::compute_vote_latency(next_vote_slot, current_slot)
} else {
0
},
lockout: Lockout::new(next_vote_slot),
};

// Once the stack is full, pop the oldest lockout and distribute rewards
if self.votes.len() == MAX_LOCKOUT_HISTORY {
let credits = self.credits_for_vote_at_index(0);
let credits = self.credits_for_vote_at_index(
0,
timely_vote_credits,
deprecate_unused_legacy_vote_plumbing,
);
let landed_vote = self.votes.pop_front().unwrap();
self.root_slot = Some(landed_vote.slot());

Expand Down Expand Up @@ -577,15 +587,20 @@ impl VoteState {
}

/// Returns the credits to award for a vote at the given lockout slot index
pub fn credits_for_vote_at_index(&self, index: usize) -> u64 {
pub fn credits_for_vote_at_index(
&self,
index: usize,
timely_vote_credits: bool,
deprecate_unused_legacy_vote_plumbing: bool,
) -> u64 {
let latency = self
.votes
.get(index)
.map_or(0, |landed_vote| landed_vote.latency);

// If latency is 0, this means that the Lockout was created and stored from a software version that did not
// store vote latencies; in this case, 1 credit is awarded
if latency == 0 {
if latency == 0 || (deprecate_unused_legacy_vote_plumbing && !timely_vote_credits) {
1
} else {
match latency.checked_sub(VOTE_CREDITS_GRACE_SLOTS) {
Expand Down
5 changes: 5 additions & 0 deletions sdk/src/feature_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,10 @@ pub mod remove_rounding_in_fee_calculation {
solana_sdk::declare_id!("BtVN7YjDzNE6Dk7kTT7YTDgMNUZTNgiSJgsdzAeTg2jF");
}

pub mod deprecate_unused_legacy_vote_plumbing {
solana_sdk::declare_id!("6Uf8S75PVh91MYgPQSHnjRAPQq6an5BDv9vomrCwDqLe");
}

lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
Expand Down Expand Up @@ -970,6 +974,7 @@ lazy_static! {
(enable_gossip_duplicate_proof_ingestion::id(), "enable gossip duplicate proof ingestion #32963"),
(enable_chained_merkle_shreds::id(), "Enable chained Merkle shreds #34916"),
(remove_rounding_in_fee_calculation::id(), "Removing unwanted rounding in fee calculation #34982"),
(deprecate_unused_legacy_vote_plumbing::id(), "Deprecate unused legacy vote tx plumbing"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()
Expand Down