Revert "Add new vote state version that replaces Lockout with LandedV… (#30817)

Revert "Add new vote state version that replaces Lockout with LandedVote to a… (#29524)"

This reverts commit d77f0a22c7.
This commit is contained in:
steviez 2023-03-21 22:54:13 +08:00 committed by GitHub
parent e5edcb9c48
commit 5344a789d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 114 additions and 624 deletions

View File

@ -42,9 +42,7 @@ use {
},
solana_vote_program::{
authorized_voters::AuthorizedVoters,
vote_state::{
BlockTimestamp, LandedVote, Lockout, MAX_EPOCH_CREDITS_HISTORY, MAX_LOCKOUT_HISTORY,
},
vote_state::{BlockTimestamp, Lockout, MAX_EPOCH_CREDITS_HISTORY, MAX_LOCKOUT_HISTORY},
},
std::{
collections::{BTreeMap, HashMap},
@ -1645,15 +1643,6 @@ impl From<&Lockout> for CliLockout {
}
}
impl From<&LandedVote> for CliLockout {
fn from(vote: &LandedVote) -> Self {
Self {
slot: vote.slot(),
confirmation_count: vote.confirmation_count(),
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CliBlockTime {

View File

@ -3,7 +3,6 @@ use {
heaviest_subtree_fork_choice::HeaviestSubtreeForkChoice,
latest_validator_votes_for_frozen_banks::LatestValidatorVotesForFrozenBanks,
progress_map::{LockoutIntervals, ProgressMap},
tower1_14_11::Tower1_14_11,
tower1_7_14::Tower1_7_14,
tower_storage::{SavedTower, SavedTowerVersions, TowerStorage},
},
@ -25,9 +24,8 @@ use {
solana_vote_program::{
vote_instruction,
vote_state::{
process_slot_vote_unchecked, process_vote_unchecked, BlockTimestamp, LandedVote,
Lockout, Vote, VoteState, VoteState1_14_11, VoteStateUpdate, VoteStateVersions,
VoteTransaction, MAX_LOCKOUT_HISTORY,
process_slot_vote_unchecked, process_vote_unchecked, BlockTimestamp, Lockout, Vote,
VoteState, VoteStateUpdate, VoteTransaction, MAX_LOCKOUT_HISTORY,
},
},
std::{
@ -141,7 +139,6 @@ pub(crate) struct ComputedBankState {
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub enum TowerVersions {
V1_17_14(Tower1_7_14),
V1_14_11(Tower1_14_11),
Current(Tower),
}
@ -159,8 +156,7 @@ impl TowerVersions {
node_pubkey: tower.node_pubkey,
threshold_depth: tower.threshold_depth,
threshold_size: tower.threshold_size,
vote_state: VoteStateVersions::V1_14_11(Box::new(tower.vote_state))
.convert_to_current(),
vote_state: tower.vote_state,
last_vote: box_last_vote,
last_vote_tx_blockhash: tower.last_vote_tx_blockhash,
last_timestamp: tower.last_timestamp,
@ -168,24 +164,12 @@ impl TowerVersions {
last_switch_threshold_check: tower.last_switch_threshold_check,
}
}
TowerVersions::V1_14_11(tower) => Tower {
node_pubkey: tower.node_pubkey,
threshold_depth: tower.threshold_depth,
threshold_size: tower.threshold_size,
vote_state: VoteStateVersions::V1_14_11(Box::new(tower.vote_state))
.convert_to_current(),
last_vote: tower.last_vote,
last_vote_tx_blockhash: tower.last_vote_tx_blockhash,
last_timestamp: tower.last_timestamp,
stray_restored_slot: tower.stray_restored_slot,
last_switch_threshold_check: tower.last_switch_threshold_check,
},
TowerVersions::Current(tower) => tower,
}
}
}
#[frozen_abi(digest = "iZi6s9BvytU3HbRsibrAD71jwMLvrqHdCjVk6qKcVvd")]
#[frozen_abi(digest = "HQoLKAJEQTuVy8nMSkVWbrH3M5xKksxdMEZHGLWbnX6w")]
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, AbiExample)]
pub struct Tower {
pub node_pubkey: Pubkey,
@ -318,30 +302,24 @@ impl Tower {
};
for vote in &vote_state.votes {
lockout_intervals
.entry(vote.lockout.last_locked_out_slot())
.entry(vote.last_locked_out_slot())
.or_insert_with(Vec::new)
.push((vote.slot(), key));
}
if key == *vote_account_pubkey {
my_latest_landed_vote = vote_state.nth_recent_lockout(0).map(|l| l.slot());
my_latest_landed_vote = vote_state.nth_recent_vote(0).map(|v| v.slot());
debug!("vote state {:?}", vote_state);
debug!(
"observed slot {}",
vote_state
.nth_recent_lockout(0)
.map(|l| l.slot())
.unwrap_or(0) as i64
vote_state.nth_recent_vote(0).map(|v| v.slot()).unwrap_or(0) as i64
);
debug!("observed root {}", vote_state.root_slot.unwrap_or(0) as i64);
datapoint_info!(
"tower-observed",
(
"slot",
vote_state
.nth_recent_lockout(0)
.map(|l| l.slot())
.unwrap_or(0),
vote_state.nth_recent_vote(0).map(|v| v.slot()).unwrap_or(0),
i64
),
("root", vote_state.root_slot.unwrap_or(0), i64)
@ -362,7 +340,7 @@ impl Tower {
process_slot_vote_unchecked(&mut vote_state, bank_slot);
for vote in &vote_state.votes {
bank_weight += vote.lockout.lockout() as u128 * voted_stake as u128;
bank_weight += vote.lockout() as u128 * voted_stake as u128;
vote_slots.insert(vote.slot());
}
@ -391,10 +369,10 @@ impl Tower {
// this vote stack is the simulated vote, so this fetch should be sufficient
// to find the last unsimulated vote.
assert_eq!(
vote_state.nth_recent_lockout(0).map(|l| l.slot()),
vote_state.nth_recent_vote(0).map(|l| l.slot()),
Some(bank_slot)
);
if let Some(vote) = vote_state.nth_recent_lockout(1) {
if let Some(vote) = vote_state.nth_recent_vote(1) {
// Update all the parents of this last vote with the stake of this vote account
Self::update_ancestor_voted_stakes(
&mut voted_stakes,
@ -502,11 +480,7 @@ impl Tower {
let vote = Vote::new(vec![vote_slot], vote_hash);
process_vote_unchecked(&mut self.vote_state, vote);
VoteTransaction::from(VoteStateUpdate::new(
self.vote_state
.votes
.iter()
.map(|vote| vote.lockout)
.collect(),
self.vote_state.votes.clone(),
self.vote_state.root_slot,
vote_hash,
))
@ -544,8 +518,7 @@ impl Tower {
/// Used for tests
pub fn increase_lockout(&mut self, confirmation_count_increase: u32) {
for vote in self.vote_state.votes.iter_mut() {
vote.lockout
.increase_confirmation_count(confirmation_count_increase);
vote.increase_confirmation_count(confirmation_count_increase);
}
}
@ -1013,24 +986,24 @@ impl Tower {
) -> bool {
let mut vote_state = self.vote_state.clone();
process_slot_vote_unchecked(&mut vote_state, slot);
let lockout = vote_state.nth_recent_lockout(self.threshold_depth);
if let Some(lockout) = lockout {
if let Some(fork_stake) = voted_stakes.get(&lockout.slot()) {
let lockout_stake = *fork_stake as f64 / total_stake as f64;
let vote = vote_state.nth_recent_vote(self.threshold_depth);
if let Some(vote) = vote {
if let Some(fork_stake) = voted_stakes.get(&vote.slot()) {
let lockout = *fork_stake as f64 / total_stake as f64;
trace!(
"fork_stake slot: {}, vote slot: {}, lockout: {} fork_stake: {} total_stake: {}",
slot, lockout.slot(), lockout_stake, fork_stake, total_stake
slot, vote.slot(), lockout, fork_stake, total_stake
);
if lockout.confirmation_count() as usize > self.threshold_depth {
if vote.confirmation_count() as usize > self.threshold_depth {
for old_vote in &self.vote_state.votes {
if old_vote.slot() == lockout.slot()
&& old_vote.confirmation_count() == lockout.confirmation_count()
if old_vote.slot() == vote.slot()
&& old_vote.confirmation_count() == vote.confirmation_count()
{
return true;
}
}
}
lockout_stake > self.threshold_size
lockout > self.threshold_size
} else {
false
}
@ -1308,7 +1281,7 @@ impl Tower {
}
}
fn initialize_lockouts<F: FnMut(&LandedVote) -> bool>(&mut self, should_retain: F) {
fn initialize_lockouts<F: FnMut(&Lockout) -> bool>(&mut self, should_retain: F) {
self.vote_state.votes.retain(should_retain);
}
@ -1356,25 +1329,6 @@ pub enum TowerError {
HardFork(Slot),
}
// Tower1_14_11 is the persisted data format for the Tower, decoupling it from VoteState::Current
// From Tower1_14_11 to Tower is not implemented because it is not an expected conversion
#[allow(clippy::from_over_into)]
impl Into<Tower1_14_11> for Tower {
fn into(self) -> Tower1_14_11 {
Tower1_14_11 {
node_pubkey: self.node_pubkey,
threshold_depth: self.threshold_depth,
threshold_size: self.threshold_size,
vote_state: VoteState1_14_11::from(self.vote_state.clone()),
last_vote: self.last_vote.clone(),
last_vote_tx_blockhash: self.last_vote_tx_blockhash,
last_timestamp: self.last_timestamp,
stray_restored_slot: self.stray_restored_slot,
last_switch_threshold_check: self.last_switch_threshold_check,
}
}
}
impl TowerError {
pub fn is_file_missing(&self) -> bool {
if let TowerError::IoError(io_err) = &self {
@ -2213,7 +2167,7 @@ pub mod test {
.vote_state
.votes
.iter()
.map(|v| v.lockout.lockout() as u128)
.map(|v| v.lockout() as u128)
.sum::<u128>()
+ root_weight;
let expected_bank_weight = 2 * vote_account_expected_weight;
@ -3207,14 +3161,8 @@ pub mod test {
#[test]
fn test_adjust_lockouts_after_replay_time_warped() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(1)));
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(0)));
tower.vote_state.votes.push_back(Lockout::new(1));
tower.vote_state.votes.push_back(Lockout::new(0));
let vote = Vote::new(vec![0], Hash::default());
tower.last_vote = VoteTransaction::from(vote);
@ -3231,14 +3179,8 @@ pub mod test {
#[test]
fn test_adjust_lockouts_after_replay_diverged_ancestor() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(1)));
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(2)));
tower.vote_state.votes.push_back(Lockout::new(1));
tower.vote_state.votes.push_back(Lockout::new(2));
let vote = Vote::new(vec![2], Hash::default());
tower.last_vote = VoteTransaction::from(vote);
@ -3261,15 +3203,9 @@ pub mod test {
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(MAX_ENTRIES - 1)));
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(0)));
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(1)));
.push_back(Lockout::new(MAX_ENTRIES - 1));
tower.vote_state.votes.push_back(Lockout::new(0));
tower.vote_state.votes.push_back(Lockout::new(1));
let vote = Vote::new(vec![1], Hash::default());
tower.last_vote = VoteTransaction::from(vote);
@ -3287,14 +3223,8 @@ pub mod test {
#[should_panic(expected = "slot_in_tower(2) < checked_slot(1)")]
fn test_adjust_lockouts_after_replay_reversed_votes() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(2)));
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(1)));
tower.vote_state.votes.push_back(Lockout::new(2));
tower.vote_state.votes.push_back(Lockout::new(1));
let vote = Vote::new(vec![1], Hash::default());
tower.last_vote = VoteTransaction::from(vote);
@ -3311,18 +3241,9 @@ pub mod test {
#[should_panic(expected = "slot_in_tower(3) < checked_slot(3)")]
fn test_adjust_lockouts_after_replay_repeated_non_root_votes() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(2)));
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(3)));
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(3)));
tower.vote_state.votes.push_back(Lockout::new(2));
tower.vote_state.votes.push_back(Lockout::new(3));
tower.vote_state.votes.push_back(Lockout::new(3));
let vote = Vote::new(vec![3], Hash::default());
tower.last_vote = VoteTransaction::from(vote);
@ -3339,18 +3260,9 @@ pub mod test {
fn test_adjust_lockouts_after_replay_vote_on_root() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower.vote_state.root_slot = Some(42);
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(42)));
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(43)));
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(44)));
tower.vote_state.votes.push_back(Lockout::new(42));
tower.vote_state.votes.push_back(Lockout::new(43));
tower.vote_state.votes.push_back(Lockout::new(44));
let vote = Vote::new(vec![44], Hash::default());
tower.last_vote = VoteTransaction::from(vote);
@ -3364,10 +3276,7 @@ pub mod test {
#[test]
fn test_adjust_lockouts_after_replay_vote_on_genesis() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(0)));
tower.vote_state.votes.push_back(Lockout::new(0));
let vote = Vote::new(vec![0], Hash::default());
tower.last_vote = VoteTransaction::from(vote);
@ -3380,14 +3289,8 @@ pub mod test {
#[test]
fn test_adjust_lockouts_after_replay_future_tower() {
let mut tower = Tower::new_for_tests(10, 0.9);
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(13)));
tower
.vote_state
.votes
.push_back(LandedVote::from(Lockout::new(14)));
tower.vote_state.votes.push_back(Lockout::new(13));
tower.vote_state.votes.push_back(Lockout::new(14));
let vote = Vote::new(vec![14], Hash::default());
tower.last_vote = VoteTransaction::from(vote);
tower.initialize_root(12);

View File

@ -71,7 +71,6 @@ pub mod snapshot_packager_service;
pub mod staked_nodes_updater_service;
pub mod stats_reporter_service;
pub mod system_monitor_service;
mod tower1_14_11;
mod tower1_7_14;
pub mod tower_storage;
pub mod tpu;

View File

@ -1,34 +0,0 @@
use {
crate::consensus::SwitchForkDecision,
solana_sdk::{clock::Slot, hash::Hash, pubkey::Pubkey},
solana_vote_program::vote_state::{
vote_state_1_14_11::VoteState1_14_11, BlockTimestamp, VoteTransaction,
},
};
#[frozen_abi(digest = "F83xHQA1wxoFDy25MTKXXmFXTc9Jbp6SXRXEPcehtKbQ")]
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, AbiExample)]
pub struct Tower1_14_11 {
pub(crate) node_pubkey: Pubkey,
pub(crate) threshold_depth: usize,
pub(crate) threshold_size: f64,
pub(crate) vote_state: VoteState1_14_11,
pub(crate) last_vote: VoteTransaction,
#[serde(skip)]
// The blockhash used in the last vote transaction, may or may not equal the
// blockhash of the voted block itself, depending if the vote slot was refreshed.
// For instance, a vote for slot 5, may be refreshed/resubmitted for inclusion in
// block 10, in which case `last_vote_tx_blockhash` equals the blockhash of 10, not 5.
pub(crate) last_vote_tx_blockhash: Hash,
pub(crate) last_timestamp: BlockTimestamp,
#[serde(skip)]
// Restored last voted slot which cannot be found in SlotHistory at replayed root
// (This is a special field for slashing-free validator restart with edge cases).
// This could be emptied after some time; but left intact indefinitely for easier
// implementation
// Further, stray slot can be stale or not. `Stale` here means whether given
// bank_forks (=~ ledger) lacks the slot or not.
pub(crate) stray_restored_slot: Option<Slot>,
#[serde(skip)]
pub(crate) last_switch_threshold_check: Option<(Slot, SwitchForkDecision)>,
}

View File

@ -6,16 +6,16 @@ use {
pubkey::Pubkey,
signature::{Signature, Signer},
},
solana_vote_program::vote_state::{vote_state_1_14_11::VoteState1_14_11, BlockTimestamp, Vote},
solana_vote_program::vote_state::{BlockTimestamp, Vote, VoteState},
};
#[frozen_abi(digest = "9Kc3Cpak93xdL8bCnEwMWA8ZLGCBNfqh9PLo1o5RiPyT")]
#[frozen_abi(digest = "8EBpwHf9gys2irNgyRCEe6A5KSh4RK875Fa46yA2NSoN")]
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, AbiExample)]
pub struct Tower1_7_14 {
pub(crate) node_pubkey: Pubkey,
pub(crate) threshold_depth: usize,
pub(crate) threshold_size: f64,
pub(crate) vote_state: VoteState1_14_11,
pub(crate) vote_state: VoteState,
pub(crate) last_vote: Vote,
#[serde(skip)]
// The blockhash used in the last vote transaction, may or may not equal the

View File

@ -1,7 +1,6 @@
use {
crate::{
consensus::{Result, Tower, TowerError, TowerVersions},
tower1_14_11::Tower1_14_11,
tower1_7_14::SavedTower1_7_14,
},
solana_sdk::{
@ -37,7 +36,7 @@ impl SavedTowerVersions {
if !t.signature.verify(node_pubkey.as_ref(), &t.data) {
return Err(TowerError::InvalidSignature);
}
bincode::deserialize(&t.data).map(TowerVersions::V1_14_11)
bincode::deserialize(&t.data).map(TowerVersions::Current)
}
};
tv.map_err(|e| e.into()).and_then(|tv: TowerVersions| {
@ -95,10 +94,7 @@ impl SavedTower {
)));
}
// SavedTower always stores its data in 1_14_11 format
let tower: Tower1_14_11 = tower.clone().into();
let data = bincode::serialize(&tower)?;
let data = bincode::serialize(tower)?;
let signature = keypair.sign_message(&data);
Ok(Self {
signature,
@ -380,8 +376,7 @@ pub mod test {
},
solana_sdk::{hash::Hash, signature::Keypair},
solana_vote_program::vote_state::{
BlockTimestamp, LandedVote, Vote, VoteState, VoteState1_14_11, VoteTransaction,
MAX_LOCKOUT_HISTORY,
BlockTimestamp, Lockout, Vote, VoteState, VoteTransaction, MAX_LOCKOUT_HISTORY,
},
tempfile::TempDir,
};
@ -394,7 +389,7 @@ pub mod test {
let mut vote_state = VoteState::default();
vote_state
.votes
.resize(MAX_LOCKOUT_HISTORY, LandedVote::default());
.resize(MAX_LOCKOUT_HISTORY, Lockout::default());
vote_state.root_slot = Some(1);
let vote = Vote::new(vec![1, 2, 3, 4], Hash::default());
@ -404,7 +399,7 @@ pub mod test {
node_pubkey,
threshold_depth: 10,
threshold_size: 0.9,
vote_state: VoteState1_14_11::from(vote_state),
vote_state,
last_vote: vote.clone(),
last_timestamp: BlockTimestamp::default(),
last_vote_tx_blockhash: Hash::default(),

View File

@ -76,13 +76,7 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins
}
let clock =
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?;
vote_state::initialize_account(
&mut me,
&vote_init,
&signers,
&clock,
&invoke_context.feature_set,
)
vote_state::initialize_account(&mut me, &vote_init, &signers, &clock)
}
VoteInstruction::Authorize(voter_pubkey, vote_authorize) => {
let clock =
@ -133,12 +127,7 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins
let node_pubkey = transaction_context.get_key_of_account_at_index(
instruction_context.get_index_of_instruction_account_in_transaction(1)?,
)?;
vote_state::update_validator_identity(
&mut me,
node_pubkey,
&signers,
&invoke_context.feature_set,
)
vote_state::update_validator_identity(&mut me, node_pubkey, &signers)
}
VoteInstruction::UpdateCommission(commission) => {
if invoke_context.feature_set.is_active(
@ -151,12 +140,7 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins
return Err(VoteError::CommissionUpdateTooLate.into());
}
}
vote_state::update_commission(
&mut me,
commission,
&signers,
&invoke_context.feature_set,
)
vote_state::update_commission(&mut me, commission, &signers)
}
VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => {
let slot_hashes =
@ -241,7 +225,6 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins
&signers,
&rent_sysvar,
clock_if_feature_active.as_deref(),
&invoke_context.feature_set,
)
}
VoteInstruction::AuthorizeChecked(vote_authorize) => {
@ -810,9 +793,7 @@ mod tests {
.convert_to_current();
assert_eq!(
vote_state.votes,
vec![vote_state::LandedVote::from(Lockout::new(
*vote.slots.last().unwrap()
))]
vec![Lockout::new(*vote.slots.last().unwrap())]
);
assert_eq!(vote_state.credits(), 0);

View File

@ -141,37 +141,6 @@ pub fn to<T: WritableAccount>(versioned: &VoteStateVersions, account: &mut T) ->
VoteState::serialize(versioned, account.data_as_mut_slice()).ok()
}
// Updates the vote account state with a new VoteState instance. This is required temporarily during the
// upgrade of vote account state from V1_14_11 to Current.
fn set_vote_account_state(
vote_account: &mut BorrowedAccount,
vote_state: VoteState,
feature_set: &FeatureSet,
) -> Result<(), InstructionError> {
// Only if vote_state_add_vote_latency feature is enabled should the new version of vote state be stored
if feature_set.is_active(&feature_set::vote_state_add_vote_latency::id()) {
// If the account is not large enough to store the vote state, then attempt a realloc to make it large enough.
// The realloc can only proceed if the vote account has balance sufficient for rent exemption at the new size.
if (vote_account.get_data().len() < VoteState::size_of())
&& (!vote_account.is_rent_exempt_at_data_length(VoteState::size_of())
|| vote_account.set_data_length(VoteState::size_of()).is_err())
{
// Account cannot be resized to the size of a vote state as it will not be rent exempt, or failed to be
// resized for other reasons. So store the V1_14_11 version.
return vote_account.set_state(&VoteStateVersions::V1_14_11(Box::new(
VoteState1_14_11::from(vote_state),
)));
}
// Vote account is large enough to store the newest version of vote state
vote_account.set_state(&VoteStateVersions::new_current(vote_state))
// Else when the vote_state_add_vote_latency feature is not enabled, then the V1_14_11 version is stored
} else {
vote_account.set_state(&VoteStateVersions::V1_14_11(Box::new(
VoteState1_14_11::from(vote_state),
)))
}
}
fn check_update_vote_state_slots_are_valid(
vote_state: &VoteState,
vote_state_update: &mut VoteStateUpdate,
@ -222,18 +191,18 @@ fn check_update_vote_state_slots_are_valid(
if is_root_fix_enabled {
let mut prev_slot = Slot::MAX;
let current_root = vote_state_update.root;
for vote in vote_state.votes.iter().rev() {
for lockout in vote_state.votes.iter().rev() {
let is_slot_bigger_than_root = current_root
.map(|current_root| vote.slot() > current_root)
.map(|current_root| lockout.slot() > current_root)
.unwrap_or(true);
// Ensure we're iterating from biggest to smallest vote in the
// current vote state
assert!(vote.slot() < prev_slot && is_slot_bigger_than_root);
if vote.slot() <= new_proposed_root {
vote_state_update.root = Some(vote.slot());
assert!(lockout.slot() < prev_slot && is_slot_bigger_than_root);
if lockout.slot() <= new_proposed_root {
vote_state_update.root = Some(lockout.slot());
break;
}
prev_slot = vote.slot();
prev_slot = lockout.slot();
}
}
}
@ -661,7 +630,7 @@ pub fn process_new_vote_state(
// lockouts are corrects.
match current_vote.slot().cmp(&new_vote.slot()) {
Ordering::Less => {
if current_vote.lockout.last_locked_out_slot() >= new_vote.slot() {
if current_vote.last_locked_out_slot() >= new_vote.slot() {
return Err(VoteError::LockoutConflict);
}
current_vote_state_index = current_vote_state_index
@ -712,10 +681,7 @@ pub fn process_new_vote_state(
vote_state.process_timestamp(last_slot, timestamp)?;
}
vote_state.root_slot = new_root;
vote_state.votes = new_state
.into_iter()
.map(|lockout| lockout.into())
.collect();
vote_state.votes = new_state;
Ok(())
}
@ -830,7 +796,7 @@ pub fn authorize<S: std::hash::BuildHasher>(
}
}
set_vote_account_state(vote_account, vote_state, feature_set)
vote_account.set_state(&VoteStateVersions::new_current(vote_state))
}
/// Update the node_pubkey, requires signature of the authorized voter
@ -838,7 +804,6 @@ pub fn update_validator_identity<S: std::hash::BuildHasher>(
vote_account: &mut BorrowedAccount,
node_pubkey: &Pubkey,
signers: &HashSet<Pubkey, S>,
feature_set: &FeatureSet,
) -> Result<(), InstructionError> {
let mut vote_state: VoteState = vote_account
.get_state::<VoteStateVersions>()?
@ -852,7 +817,7 @@ pub fn update_validator_identity<S: std::hash::BuildHasher>(
vote_state.node_pubkey = *node_pubkey;
set_vote_account_state(vote_account, vote_state, feature_set)
vote_account.set_state(&VoteStateVersions::new_current(vote_state))
}
/// Update the vote account's commission
@ -860,7 +825,6 @@ pub fn update_commission<S: std::hash::BuildHasher>(
vote_account: &mut BorrowedAccount,
commission: u8,
signers: &HashSet<Pubkey, S>,
feature_set: &FeatureSet,
) -> Result<(), InstructionError> {
let mut vote_state: VoteState = vote_account
.get_state::<VoteStateVersions>()?
@ -871,7 +835,7 @@ pub fn update_commission<S: std::hash::BuildHasher>(
vote_state.commission = commission;
set_vote_account_state(vote_account, vote_state, feature_set)
vote_account.set_state(&VoteStateVersions::new_current(vote_state))
}
/// Given the current slot and epoch schedule, determine if a commission change
@ -911,7 +875,6 @@ pub fn withdraw<S: std::hash::BuildHasher>(
signers: &HashSet<Pubkey, S>,
rent_sysvar: &Rent,
clock: Option<&Clock>,
feature_set: &FeatureSet,
) -> Result<(), InstructionError> {
let mut vote_account = instruction_context
.try_borrow_instruction_account(transaction_context, vote_account_index)?;
@ -944,7 +907,7 @@ pub fn withdraw<S: std::hash::BuildHasher>(
} else {
// Deinitialize upon zero-balance
datapoint_debug!("vote-account-close", ("allow", 1, i64));
set_vote_account_state(&mut vote_account, VoteState::default(), feature_set)?;
vote_account.set_state(&VoteStateVersions::new_current(VoteState::default()))?;
}
} else {
let min_rent_exempt_balance = rent_sysvar.minimum_balance(vote_account.get_data().len());
@ -969,7 +932,6 @@ pub fn initialize_account<S: std::hash::BuildHasher>(
vote_init: &VoteInit,
signers: &HashSet<Pubkey, S>,
clock: &Clock,
feature_set: &FeatureSet,
) -> Result<(), InstructionError> {
if vote_account.get_data().len() != VoteState::size_of() {
return Err(InstructionError::InvalidAccountData);
@ -983,7 +945,9 @@ pub fn initialize_account<S: std::hash::BuildHasher>(
// node must agree to accept this vote account
verify_authorized_signer(&vote_init.node_pubkey, signers)?;
set_vote_account_state(vote_account, VoteState::new(vote_init, clock), feature_set)
vote_account.set_state(&VoteStateVersions::new_current(VoteState::new(
vote_init, clock,
)))
}
fn verify_and_get_vote_state<S: std::hash::BuildHasher>(
@ -1028,7 +992,7 @@ pub fn process_vote_with_account<S: std::hash::BuildHasher>(
.ok_or(VoteError::EmptySlots)
.and_then(|slot| vote_state.process_timestamp(*slot, timestamp))?;
}
set_vote_account_state(vote_account, vote_state, feature_set)
vote_account.set_state(&VoteStateVersions::new_current(vote_state))
}
pub fn process_vote_state_update<S: std::hash::BuildHasher>(
@ -1047,7 +1011,7 @@ pub fn process_vote_state_update<S: std::hash::BuildHasher>(
vote_state_update,
Some(feature_set),
)?;
set_vote_account_state(vote_account, vote_state, feature_set)
vote_account.set_state(&VoteStateVersions::new_current(vote_state))
}
pub fn do_process_vote_state_update(
@ -1073,12 +1037,6 @@ pub fn do_process_vote_state_update(
)
}
// This function is used:
// a. In many tests.
// b. In the genesis tool that initializes a cluster to create the bootstrap validator.
// c. In the ledger tool when creating bootstrap vote accounts.
// In all cases, initializing with the 1_14_11 version of VoteState is safest, as this version will in-place upgrade
// the first time it is altered by a vote transaction.
pub fn create_account_with_authorized(
node_pubkey: &Pubkey,
authorized_voter: &Pubkey,
@ -1098,8 +1056,8 @@ pub fn create_account_with_authorized(
&Clock::default(),
);
let version1_14_11 = VoteStateVersions::V1_14_11(Box::new(VoteState1_14_11::from(vote_state)));
VoteState::serialize(&version1_14_11, vote_account.data_as_mut_slice()).unwrap();
let versioned = VoteStateVersions::new_current(vote_state);
VoteState::serialize(&versioned, vote_account.data_as_mut_slice()).unwrap();
vote_account
}
@ -1121,7 +1079,7 @@ mod tests {
crate::vote_state,
solana_sdk::{
account::AccountSharedData, account_utils::StateMut, clock::DEFAULT_SLOTS_PER_EPOCH,
hash::hash, transaction_context::InstructionAccount,
hash::hash,
},
std::cell::RefCell,
test_case::test_case,
@ -1156,138 +1114,6 @@ mod tests {
)
}
#[test]
fn test_vote_state_upgrade_from_1_14_11() {
let mut feature_set = FeatureSet::default();
// Create an initial vote account that is sized for the 1_14_11 version of vote state, and has only the
// required lamports for rent exempt minimum at that size
let node_pubkey = solana_sdk::pubkey::new_rand();
let withdrawer_pubkey = solana_sdk::pubkey::new_rand();
let mut vote_state = VoteState::new(
&VoteInit {
node_pubkey,
authorized_voter: withdrawer_pubkey,
authorized_withdrawer: withdrawer_pubkey,
commission: 10,
},
&Clock::default(),
);
// Pretend that prior epochs completed with credits
vote_state.increment_credits(1, 100);
vote_state.increment_credits(2, 200);
vote_state.increment_credits(3, 300);
// Pretend that votes happened
vec![
100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
134, 135,
]
.into_iter()
.for_each(|v| vote_state.process_next_vote_slot(v, 4));
let version1_14_11_serialized = bincode::serialize(&VoteStateVersions::V1_14_11(Box::new(
VoteState1_14_11::from(vote_state.clone()),
)))
.unwrap();
let version1_14_11_serialized_len = version1_14_11_serialized.len();
let rent = Rent::default();
let lamports = rent.minimum_balance(version1_14_11_serialized_len);
let mut vote_account =
AccountSharedData::new(lamports, version1_14_11_serialized_len, &id());
vote_account.set_data(version1_14_11_serialized);
// Create a fake TransactionContext with a fake InstructionContext with a single account which is the
// vote account that was just created
let transaction_context =
TransactionContext::new(vec![(node_pubkey, vote_account)], None, 0, 0);
let mut instruction_context = InstructionContext::default();
instruction_context.configure(
&[0],
&[InstructionAccount {
index_in_transaction: 0,
index_in_caller: 0,
index_in_callee: 0,
is_signer: false,
is_writable: true,
}],
&[],
);
// Get the BorrowedAccount from the InstructionContext which is what is used to manipulate and inspect account
// state
let mut borrowed_account = instruction_context
.try_borrow_instruction_account(&transaction_context, 0)
.unwrap();
// Ensure that the vote state started out at 1_14_11
let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
assert!(matches!(vote_state_version, VoteStateVersions::V1_14_11(_)));
// Convert the vote state to current as would occur during vote instructions
let converted_vote_state = vote_state_version.convert_to_current();
// Check to make sure that the vote_state is unchanged
assert!(vote_state == converted_vote_state);
let vote_state = converted_vote_state;
// Now re-set the vote account state; because the feature is not enabled, the old 1_14_11 format should be
// written out
assert_eq!(
set_vote_account_state(&mut borrowed_account, vote_state.clone(), &feature_set),
Ok(())
);
let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
assert!(matches!(vote_state_version, VoteStateVersions::V1_14_11(_)));
// Convert the vote state to current as would occur during vote instructions
let converted_vote_state = vote_state_version.convert_to_current();
// Check to make sure that the vote_state is unchanged
assert_eq!(vote_state, converted_vote_state);
let vote_state = converted_vote_state;
// Test that when the feature is enabled, if the vote account does not have sufficient lamports to realloc,
// the old vote state is written out
feature_set.activate(&feature_set::vote_state_add_vote_latency::id(), 1);
assert_eq!(
set_vote_account_state(&mut borrowed_account, vote_state.clone(), &feature_set),
Ok(())
);
let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
assert!(matches!(vote_state_version, VoteStateVersions::V1_14_11(_)));
// Convert the vote state to current as would occur during vote instructions
let converted_vote_state = vote_state_version.convert_to_current();
// Check to make sure that the vote_state is unchanged
assert_eq!(vote_state, converted_vote_state);
let vote_state = converted_vote_state;
// Test that when the feature is enabled, if the vote account does have sufficient lamports, the
// new vote state is written out
assert_eq!(
borrowed_account.set_lamports(rent.minimum_balance(VoteState::size_of())),
Ok(())
);
assert_eq!(
set_vote_account_state(&mut borrowed_account, vote_state.clone(), &feature_set),
Ok(())
);
let vote_state_version = borrowed_account.get_state::<VoteStateVersions>().unwrap();
assert!(matches!(vote_state_version, VoteStateVersions::Current(_)));
// Convert the vote state to current as would occur during vote instructions
let converted_vote_state = vote_state_version.convert_to_current();
// Check to make sure that the vote_state is unchanged
assert_eq!(vote_state, converted_vote_state);
}
#[test]
fn test_vote_lockout() {
let (_vote_pubkey, vote_account) = create_test_account();
@ -1315,12 +1141,7 @@ mod tests {
assert_eq!(Some(top_vote), vote_state.root_slot);
// Expire everything except the first vote
let slot = vote_state
.votes
.front()
.unwrap()
.lockout
.last_locked_out_slot();
let slot = vote_state.votes.front().unwrap().last_locked_out_slot();
process_slot_vote_unchecked(&mut vote_state, slot);
// First vote and new vote are both stored for a total of 2 votes
assert_eq!(vote_state.votes.len(), 2);
@ -1366,7 +1187,7 @@ mod tests {
assert_eq!(vote_state.votes[0].confirmation_count(), 3);
// Expire the second and third votes
let expire_slot = vote_state.votes[1].slot() + vote_state.votes[1].lockout.lockout() + 1;
let expire_slot = vote_state.votes[1].slot() + vote_state.votes[1].lockout() + 1;
process_slot_vote_unchecked(&mut vote_state, expire_slot);
assert_eq!(vote_state.votes.len(), 2);
@ -1411,13 +1232,13 @@ mod tests {
process_slot_vote_unchecked(&mut vote_state, 0);
process_slot_vote_unchecked(&mut vote_state, 1);
process_slot_vote_unchecked(&mut vote_state, 0);
assert_eq!(vote_state.nth_recent_lockout(0).unwrap().slot(), 1);
assert_eq!(vote_state.nth_recent_lockout(1).unwrap().slot(), 0);
assert!(vote_state.nth_recent_lockout(2).is_none());
assert_eq!(vote_state.nth_recent_vote(0).unwrap().slot(), 1);
assert_eq!(vote_state.nth_recent_vote(1).unwrap().slot(), 0);
assert!(vote_state.nth_recent_vote(2).is_none());
}
#[test]
fn test_nth_recent_lockout() {
fn test_nth_recent_vote() {
let voter_pubkey = solana_sdk::pubkey::new_rand();
let mut vote_state = vote_state_new_for_test(&voter_pubkey);
for i in 0..MAX_LOCKOUT_HISTORY {
@ -1425,11 +1246,11 @@ mod tests {
}
for i in 0..(MAX_LOCKOUT_HISTORY - 1) {
assert_eq!(
vote_state.nth_recent_lockout(i).unwrap().slot() as usize,
vote_state.nth_recent_vote(i).unwrap().slot() as usize,
MAX_LOCKOUT_HISTORY - i - 1,
);
}
assert!(vote_state.nth_recent_lockout(MAX_LOCKOUT_HISTORY).is_none());
assert!(vote_state.nth_recent_vote(MAX_LOCKOUT_HISTORY).is_none());
}
fn check_lockouts(vote_state: &VoteState) {
@ -1439,10 +1260,7 @@ mod tests {
.len()
.checked_sub(i)
.expect("`i` is less than `vote_state.votes.len()`");
assert_eq!(
vote.lockout.lockout(),
INITIAL_LOCKOUT.pow(num_votes as u32) as u64
);
assert_eq!(vote.lockout(), INITIAL_LOCKOUT.pow(num_votes as u32) as u64);
}
}
@ -1657,24 +1475,6 @@ mod tests {
);
}
pub fn process_new_vote_state_from_votes(
vote_state: &mut VoteState,
new_state: VecDeque<LandedVote>,
new_root: Option<Slot>,
timestamp: Option<i64>,
epoch: Epoch,
feature_set: Option<&FeatureSet>,
) -> Result<(), VoteError> {
process_new_vote_state(
vote_state,
new_state.into_iter().map(|vote| vote.lockout).collect(),
new_root,
timestamp,
epoch,
feature_set,
)
}
// Test vote credit updates after "one credit per slot" feature is enabled
#[test]
fn test_vote_state_update_increment_credits() {
@ -1737,7 +1537,7 @@ mod tests {
// Now use the resulting new vote state to perform a vote state update on vote_state
assert_eq!(
process_new_vote_state_from_votes(
process_new_vote_state(
&mut vote_state,
vote_state_after_vote.votes,
vote_state_after_vote.root_slot,
@ -1793,7 +1593,7 @@ mod tests {
let current_epoch = vote_state2.current_epoch();
assert_eq!(
process_new_vote_state_from_votes(
process_new_vote_state(
&mut vote_state1,
vote_state2.votes.clone(),
lesser_root,
@ -1807,7 +1607,7 @@ mod tests {
// Trying to set root to None should error
let none_root = None;
assert_eq!(
process_new_vote_state_from_votes(
process_new_vote_state(
&mut vote_state1,
vote_state2.votes.clone(),
none_root,
@ -2048,7 +1848,7 @@ mod tests {
process_slot_vote_unchecked(&mut vote_state2, new_vote as Slot);
assert_ne!(vote_state1.root_slot, vote_state2.root_slot);
process_new_vote_state_from_votes(
process_new_vote_state(
&mut vote_state1,
vote_state2.votes.clone(),
vote_state2.root_slot,
@ -2106,7 +1906,7 @@ mod tests {
);
// See that on-chain vote state can update properly
process_new_vote_state_from_votes(
process_new_vote_state(
&mut vote_state1,
vote_state2.votes.clone(),
vote_state2.root_slot,
@ -2148,7 +1948,7 @@ mod tests {
// See that on-chain vote state can update properly
assert_eq!(
process_new_vote_state_from_votes(
process_new_vote_state(
&mut vote_state1,
vote_state2.votes.clone(),
vote_state2.root_slot,
@ -2190,7 +1990,7 @@ mod tests {
// Both vote states contain `5`, but `5` is not part of the common prefix
// of both vote states. However, the violation should still be detected.
assert_eq!(
process_new_vote_state_from_votes(
process_new_vote_state(
&mut vote_state1,
vote_state2.votes.clone(),
vote_state2.root_slot,
@ -2224,7 +2024,7 @@ mod tests {
// Slot 1 has been expired by 10, but is kept alive by its descendant
// 9 which has not been expired yet.
assert_eq!(vote_state2.votes[0].slot(), 1);
assert_eq!(vote_state2.votes[0].lockout.last_locked_out_slot(), 9);
assert_eq!(vote_state2.votes[0].last_locked_out_slot(), 9);
assert_eq!(
vote_state2
.votes
@ -2235,7 +2035,7 @@ mod tests {
);
// Should be able to update vote_state1
process_new_vote_state_from_votes(
process_new_vote_state(
&mut vote_state1,
vote_state2.votes.clone(),
vote_state2.root_slot,
@ -2276,15 +2076,15 @@ mod tests {
Err(VoteError::LockoutConflict)
);
let good_votes: VecDeque<LandedVote> = vec![
Lockout::new_with_confirmation_count(2, 5).into(),
Lockout::new_with_confirmation_count(15, 1).into(),
let good_votes: VecDeque<Lockout> = vec![
Lockout::new_with_confirmation_count(2, 5),
Lockout::new_with_confirmation_count(15, 1),
]
.into_iter()
.collect();
let current_epoch = vote_state1.current_epoch();
process_new_vote_state_from_votes(
process_new_vote_state(
&mut vote_state1,
good_votes.clone(),
root,
@ -2326,11 +2126,7 @@ mod tests {
let vote = Vote::new(vec![old_vote_slot, vote_slot], vote_slot_hash);
process_vote(&mut vote_state, &vote, &slot_hashes, 0, Some(&feature_set)).unwrap();
assert_eq!(
vote_state
.votes
.into_iter()
.map(|vote| vote.lockout)
.collect::<Vec<Lockout>>(),
vote_state.votes.into_iter().collect::<Vec<Lockout>>(),
vec![Lockout::new_with_confirmation_count(vote_slot, 1)]
);
}
@ -2497,11 +2293,7 @@ mod tests {
.is_ok());
assert_eq!(vote_state.root_slot, expected_root);
assert_eq!(
vote_state
.votes
.into_iter()
.map(|vote| vote.lockout)
.collect::<Vec<Lockout>>(),
vote_state.votes.into_iter().collect::<Vec<Lockout>>(),
expected_vote_state,
);
}

View File

@ -20,8 +20,6 @@ use {
};
mod vote_state_0_23_5;
pub mod vote_state_1_14_11;
pub use vote_state_1_14_11::*;
pub mod vote_state_versions;
pub use vote_state_versions::*;
@ -107,40 +105,6 @@ impl Lockout {
}
}
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Copy, Clone, AbiExample)]
pub struct LandedVote {
// Latency is the difference in slot number between the slot that was voted on (lockout.slot) and the slot in
// which the vote that added this Lockout landed. For votes which were cast before versions of the validator
// software which recorded vote latencies, latency is recorded as 0.
pub latency: u8,
pub lockout: Lockout,
}
impl LandedVote {
pub fn slot(&self) -> Slot {
self.lockout.slot
}
pub fn confirmation_count(&self) -> u32 {
self.lockout.confirmation_count
}
}
impl From<LandedVote> for Lockout {
fn from(landed_vote: LandedVote) -> Self {
landed_vote.lockout
}
}
impl From<Lockout> for LandedVote {
fn from(lockout: Lockout) -> Self {
Self {
latency: 0,
lockout,
}
}
}
#[frozen_abi(digest = "GwJfVFsATSj7nvKwtUkHYzqPRaPY6SLxPGXApuCya3x5")]
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
pub struct VoteStateUpdate {
@ -274,7 +238,7 @@ impl<I> CircBuf<I> {
}
}
#[frozen_abi(digest = "EeenjJaSrm9hRM39gK6raRNtzG61hnk7GciUCJJRDUSQ")]
#[frozen_abi(digest = "4oxo6mBc8zrZFA89RgKsNyMqqM52iVrCphsWfaHjaAAY")]
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
pub struct VoteState {
/// the node that votes in this account
@ -286,7 +250,7 @@ pub struct VoteState {
/// payout should be given to this VoteAccount
pub commission: u8,
pub votes: VecDeque<LandedVote>,
pub votes: VecDeque<Lockout>,
// This usually the last Lockout which was popped from self.votes.
// However, it can be arbitrary slot, when being used inside Tower
@ -338,7 +302,7 @@ impl VoteState {
/// Upper limit on the size of the Vote State
/// when votes.len() is MAX_LOCKOUT_HISTORY.
pub const fn size_of() -> usize {
3762 // see test_vote_state_size_of.
3731 // see test_vote_state_size_of.
}
pub fn deserialize(_input: &[u8]) -> Result<Self, InstructionError> {
@ -398,7 +362,7 @@ impl VoteState {
/// Returns if the vote state contains a slot `candidate_slot`
pub fn contains_slot(&self, candidate_slot: Slot) -> bool {
self.votes
.binary_search_by(|vote| vote.slot().cmp(&candidate_slot))
.binary_search_by(|lockout| lockout.slot().cmp(&candidate_slot))
.is_ok()
}
@ -410,7 +374,7 @@ impl VoteState {
}
VoteState {
votes: VecDeque::from(vec![LandedVote::default(); MAX_LOCKOUT_HISTORY]),
votes: VecDeque::from(vec![Lockout::default(); MAX_LOCKOUT_HISTORY]),
root_slot: Some(std::u64::MAX),
epoch_credits: vec![(0, 0, 0); MAX_EPOCH_CREDITS_HISTORY],
authorized_voters,
@ -427,7 +391,7 @@ impl VoteState {
return;
}
let lockout = Lockout::new(next_vote_slot);
let vote = Lockout::new(next_vote_slot);
self.pop_expired_votes(next_vote_slot);
@ -438,7 +402,7 @@ impl VoteState {
self.increment_credits(epoch, 1);
}
self.votes.push_back(lockout.into());
self.votes.push_back(vote);
self.double_lockouts();
}
@ -471,21 +435,21 @@ impl VoteState {
self.epoch_credits.last().unwrap().1.saturating_add(credits);
}
pub fn nth_recent_lockout(&self, position: usize) -> Option<&Lockout> {
pub fn nth_recent_vote(&self, position: usize) -> Option<&Lockout> {
if position < self.votes.len() {
let pos = self
.votes
.len()
.checked_sub(position)
.and_then(|pos| pos.checked_sub(1))?;
self.votes.get(pos).map(|vote| &vote.lockout)
self.votes.get(pos)
} else {
None
}
}
pub fn last_lockout(&self) -> Option<&Lockout> {
self.votes.back().map(|vote| &vote.lockout)
self.votes.back()
}
pub fn last_voted_slot(&self) -> Option<Slot> {
@ -615,11 +579,8 @@ impl VoteState {
for (i, v) in self.votes.iter_mut().enumerate() {
// Don't increase the lockout for this vote until we get more confirmations
// than the max number of confirmations this vote has seen
if stack_depth >
i.checked_add(v.confirmation_count() as usize)
.expect("`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`")
{
v.lockout.increase_confirmation_count(1);
if stack_depth > i.checked_add(v.confirmation_count() as usize).expect("`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`") {
v.increase_confirmation_count(1);
}
}
}
@ -758,7 +719,7 @@ mod tests {
let mut vote_state = VoteState::default();
vote_state
.votes
.resize(MAX_LOCKOUT_HISTORY, LandedVote::default());
.resize(MAX_LOCKOUT_HISTORY, Lockout::default());
vote_state.root_slot = Some(1);
let versioned = VoteStateVersions::new_current(vote_state);
assert!(VoteState::serialize(&versioned, &mut buffer[0..4]).is_err());

View File

@ -1,55 +0,0 @@
use super::*;
#[frozen_abi(digest = "CZTgLymuevXjAx6tM8X8T5J3MCx9AkEsFSmu4FJrEpkG")]
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
pub struct VoteState1_14_11 {
/// the node that votes in this account
pub node_pubkey: Pubkey,
/// the signer for withdrawals
pub authorized_withdrawer: Pubkey,
/// percentage (0-100) that represents what part of a rewards
/// payout should be given to this VoteAccount
pub commission: u8,
pub votes: VecDeque<Lockout>,
// This usually the last Lockout which was popped from self.votes.
// However, it can be arbitrary slot, when being used inside Tower
pub root_slot: Option<Slot>,
/// the signer for vote transactions
pub authorized_voters: AuthorizedVoters,
/// history of prior authorized voters and the epochs for which
/// they were set, the bottom end of the range is inclusive,
/// the top of the range is exclusive
pub prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>,
/// history of how many credits earned by the end of each epoch
/// each tuple is (Epoch, credits, prev_credits)
pub epoch_credits: Vec<(Epoch, u64, u64)>,
/// most recent timestamp submitted with a vote
pub last_timestamp: BlockTimestamp,
}
impl From<VoteState> for VoteState1_14_11 {
fn from(vote_state: VoteState) -> Self {
Self {
node_pubkey: vote_state.node_pubkey,
authorized_withdrawer: vote_state.authorized_withdrawer,
commission: vote_state.commission,
votes: vote_state
.votes
.into_iter()
.map(|landed_vote| landed_vote.into())
.collect(),
root_slot: vote_state.root_slot,
authorized_voters: vote_state.authorized_voters,
prior_voters: vote_state.prior_voters,
epoch_credits: vote_state.epoch_credits,
last_timestamp: vote_state.last_timestamp,
}
}
}

View File

@ -1,9 +1,8 @@
use super::{vote_state_0_23_5::VoteState0_23_5, vote_state_1_14_11::VoteState1_14_11, *};
use super::{vote_state_0_23_5::VoteState0_23_5, *};
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub enum VoteStateVersions {
V0_23_5(Box<VoteState0_23_5>),
V1_14_11(Box<VoteState1_14_11>),
Current(Box<VoteState>),
}
@ -28,7 +27,7 @@ impl VoteStateVersions {
/// payout should be given to this VoteAccount
commission: state.commission,
votes: Self::landed_votes_from_lockouts(state.votes),
votes: state.votes.clone(),
root_slot: state.root_slot,
@ -48,41 +47,16 @@ impl VoteStateVersions {
last_timestamp: state.last_timestamp.clone(),
}
}
VoteStateVersions::V1_14_11(state) => VoteState {
node_pubkey: state.node_pubkey,
authorized_withdrawer: state.authorized_withdrawer,
commission: state.commission,
votes: Self::landed_votes_from_lockouts(state.votes),
root_slot: state.root_slot,
authorized_voters: state.authorized_voters.clone(),
prior_voters: CircBuf::default(),
epoch_credits: state.epoch_credits,
last_timestamp: state.last_timestamp,
},
VoteStateVersions::Current(state) => *state,
}
}
fn landed_votes_from_lockouts(lockouts: VecDeque<Lockout>) -> VecDeque<LandedVote> {
lockouts.into_iter().map(|lockout| lockout.into()).collect()
}
pub fn is_uninitialized(&self) -> bool {
match self {
VoteStateVersions::V0_23_5(vote_state) => {
vote_state.authorized_voter == Pubkey::default()
}
VoteStateVersions::V1_14_11(vote_state) => vote_state.authorized_voters.is_empty(),
VoteStateVersions::Current(vote_state) => vote_state.authorized_voters.is_empty(),
}
}

View File

@ -634,10 +634,6 @@ pub mod remove_bpf_loader_incorrect_program_id {
solana_sdk::declare_id!("2HmTkCj9tXuPE4ueHzdD7jPeMf9JGCoZh5AsyoATiWEe");
}
pub mod vote_state_add_vote_latency {
solana_sdk::declare_id!("7axKe5BTYBDD87ftzWbk5DfzWMGyRvqmWTduuo22Yaqy");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -791,7 +787,6 @@ lazy_static! {
(switch_to_new_elf_parser::id(), "switch to new ELF parser #30497"),
(round_up_heap_size::id(), "round up heap size when calculating heap cost #30679"),
(remove_bpf_loader_incorrect_program_id::id(), "stop incorrectly throwing IncorrectProgramId in bpf_loader #30747"),
(vote_state_add_vote_latency::id(), "replace Lockout with LandedVote (including vote latency) in vote state"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()

View File

@ -874,16 +874,6 @@ impl<'a> BorrowedAccount<'a> {
Ok(())
}
// Returns whether or the lamports currently in the account is sufficient for rent exemption should the
// data be resized to the given size
#[cfg(not(target_os = "solana"))]
pub fn is_rent_exempt_at_data_length(&self, data_length: usize) -> bool {
self.transaction_context
.rent
.unwrap_or_default()
.is_exempt(self.get_lamports(), data_length)
}
/// Returns whether this account is executable (transaction wide)
#[inline]
pub fn is_executable(&self) -> bool {