diff --git a/account-decoder/src/parse_vote.rs b/account-decoder/src/parse_vote.rs index c1310f8be00339..7f7831f2f3ff48 100644 --- a/account-decoder/src/parse_vote.rs +++ b/account-decoder/src/parse_vote.rs @@ -8,8 +8,7 @@ use { }; pub fn parse_vote(data: &[u8]) -> Result { - let mut vote_state = - VoteState::deserialize_with_bincode(data).map_err(ParseAccountError::from)?; + let mut vote_state = VoteState::deserialize(data).map_err(ParseAccountError::from)?; let epoch_credits = vote_state .epoch_credits() .iter() diff --git a/programs/vote/src/vote_state/mod.rs b/programs/vote/src/vote_state/mod.rs index 058b385f309873..6fe297f22ac5cd 100644 --- a/programs/vote/src/vote_state/mod.rs +++ b/programs/vote/src/vote_state/mod.rs @@ -133,7 +133,7 @@ impl From for VoteTransaction { // utility function, used by Stakes, tests pub fn from(account: &T) -> Option { - VoteState::deserialize_with_bincode(account.data()).ok() + VoteState::deserialize(account.data()).ok() } // utility function, used by Stakes, tests @@ -810,9 +810,7 @@ pub fn authorize( clock: &Clock, feature_set: &FeatureSet, ) -> Result<(), InstructionError> { - let mut vote_state: VoteState = vote_account - .get_state::()? - .convert_to_current(); + let mut vote_state = VoteState::deserialize(vote_account.get_data())?; match vote_authorize { VoteAuthorize::Voter => { @@ -853,9 +851,7 @@ pub fn update_validator_identity( signers: &HashSet, feature_set: &FeatureSet, ) -> Result<(), InstructionError> { - let mut vote_state: VoteState = vote_account - .get_state::()? - .convert_to_current(); + let mut vote_state = VoteState::deserialize(vote_account.get_data())?; // current authorized withdrawer must say "yay" verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?; @@ -882,8 +878,8 @@ pub fn update_commission( let enforce_commission_update_rule = if feature_set.is_active(&feature_set::allow_commission_decrease_at_any_time::id()) { - if let Ok(decoded_vote_state) = vote_account.get_state::() { - vote_state = Some(decoded_vote_state.convert_to_current()); + if let Ok(decoded_vote_state) = VoteState::deserialize(vote_account.get_data()) { + vote_state = Some(decoded_vote_state); is_commission_increase(vote_state.as_ref().unwrap(), commission) } else { true @@ -904,9 +900,7 @@ pub fn update_commission( let mut vote_state = match vote_state { Some(vote_state) => vote_state, - None => vote_account - .get_state::()? - .convert_to_current(), + None => VoteState::deserialize(vote_account.get_data())?, }; // current authorized withdrawer must say "yay" @@ -963,9 +957,7 @@ pub fn withdraw( ) -> Result<(), InstructionError> { let mut vote_account = instruction_context .try_borrow_instruction_account(transaction_context, vote_account_index)?; - let vote_state: VoteState = vote_account - .get_state::()? - .convert_to_current(); + let vote_state = VoteState::deserialize(vote_account.get_data())?; verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?; @@ -1027,9 +1019,9 @@ pub fn initialize_account( { return Err(InstructionError::InvalidAccountData); } - let versioned = vote_account.get_state::()?; - if !versioned.is_uninitialized() { + let vote_state = VoteState::deserialize(vote_account.get_data())?; + if !vote_state.is_uninitialized() { return Err(InstructionError::AccountAlreadyInitialized); } @@ -1044,13 +1036,11 @@ fn verify_and_get_vote_state( clock: &Clock, signers: &HashSet, ) -> Result { - let versioned = vote_account.get_state::()?; - - if versioned.is_uninitialized() { + let mut vote_state = VoteState::deserialize(vote_account.get_data())?; + if vote_state.is_uninitialized() { return Err(InstructionError::UninitializedAccount); } - let mut vote_state = versioned.convert_to_current(); let authorized_voter = vote_state.get_and_update_authorized_voter(clock.epoch)?; verify_authorized_signer(&authorized_voter, signers)?; diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index ef3148a5e7d5c7..eb040b3b79cade 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -2650,7 +2650,7 @@ impl Bank { // vote_accounts_cache_miss_count is shown to be always zero. let account = self.get_account_with_fixed_root(vote_pubkey)?; if account.owner() == &solana_vote_program - && VoteState::deserialize_with_bincode(account.data()).is_ok() + && VoteState::deserialize(account.data()).is_ok() { vote_accounts_cache_miss_count.fetch_add(1, Relaxed); } diff --git a/sdk/program/src/vote/authorized_voters.rs b/sdk/program/src/vote/authorized_voters.rs index 9920391146b2c2..44d43954295304 100644 --- a/sdk/program/src/vote/authorized_voters.rs +++ b/sdk/program/src/vote/authorized_voters.rs @@ -64,6 +64,11 @@ impl AuthorizedVoters { self.authorized_voters.is_empty() } + // when an uninitialized V0_23_5 account is converted to current, it inserts a null voter + pub fn is_uninitialized(&self) -> bool { + self.is_empty() || (self.len() == 1 && self.first() == Some((&0, &Pubkey::default()))) + } + pub fn first(&self) -> Option<(&u64, &Pubkey)> { self.authorized_voters.iter().next() } diff --git a/sdk/program/src/vote/state/mod.rs b/sdk/program/src/vote/state/mod.rs index 39018e213a12af..d9693b967e5c2b 100644 --- a/sdk/program/src/vote/state/mod.rs +++ b/sdk/program/src/vote/state/mod.rs @@ -382,20 +382,6 @@ impl VoteState { Ok(vote_state) } - // this only exists for the sake of the feature gated upgrade to the new parser; do not use it - #[doc(hidden)] - #[allow(clippy::used_underscore_binding)] - pub fn deserialize_with_bincode(_input: &[u8]) -> Result { - #[cfg(not(target_os = "solana"))] - { - bincode::deserialize::(_input) - .map(|versioned| versioned.convert_to_current()) - .map_err(|_| InstructionError::InvalidAccountData) - } - #[cfg(target_os = "solana")] - unimplemented!() - } - /// Deserializes the input buffer into the provided `VoteState` /// /// This function is exposed to allow deserialization in a BPF context directly into boxed memory. @@ -777,6 +763,10 @@ impl VoteState { data.len() == VoteState::size_of() && data[VERSION_OFFSET..DEFAULT_PRIOR_VOTERS_END] != [0; DEFAULT_PRIOR_VOTERS_OFFSET] } + + pub fn is_uninitialized(&self) -> bool { + self.authorized_voters.is_uninitialized() + } } pub mod serde_compact_vote_state_update { diff --git a/sdk/program/src/vote/state/vote_state_1_14_11.rs b/sdk/program/src/vote/state/vote_state_1_14_11.rs index 4b68ced36524d6..3059698d6f6f4c 100644 --- a/sdk/program/src/vote/state/vote_state_1_14_11.rs +++ b/sdk/program/src/vote/state/vote_state_1_14_11.rs @@ -57,6 +57,10 @@ impl VoteState1_14_11 { data.len() == VoteState1_14_11::size_of() && data[VERSION_OFFSET..DEFAULT_PRIOR_VOTERS_END] != [0; DEFAULT_PRIOR_VOTERS_OFFSET] } + + pub fn is_uninitialized(&self) -> bool { + self.authorized_voters.is_uninitialized() + } } impl From for VoteState1_14_11 { diff --git a/sdk/program/src/vote/state/vote_state_deserialize.rs b/sdk/program/src/vote/state/vote_state_deserialize.rs index 3694f25af37564..f4628332c2f2c3 100644 --- a/sdk/program/src/vote/state/vote_state_deserialize.rs +++ b/sdk/program/src/vote/state/vote_state_deserialize.rs @@ -1,7 +1,6 @@ use { crate::{ instruction::InstructionError, - pubkey::Pubkey, serialize_utils::cursor::*, vote::state::{BlockTimestamp, LandedVote, Lockout, VoteState, MAX_ITEMS}, }, @@ -87,21 +86,12 @@ fn read_prior_voters_into>( if !is_empty { cursor.set_position(prior_voters_position); - let mut encountered_null_voter = false; for i in 0..MAX_ITEMS { let prior_voter = read_pubkey(cursor)?; let from_epoch = read_u64(cursor)?; let until_epoch = read_u64(cursor)?; - let item = (prior_voter, from_epoch, until_epoch); - - if item == (Pubkey::default(), 0, 0) { - encountered_null_voter = true; - } else if encountered_null_voter { - // `prior_voters` should never be sparse - return Err(InstructionError::InvalidAccountData); - } else { - vote_state.prior_voters.buf[i] = item; - } + + vote_state.prior_voters.buf[i] = (prior_voter, from_epoch, until_epoch); } vote_state.prior_voters.idx = read_u64(cursor)? as usize; diff --git a/sdk/program/src/vote/state/vote_state_versions.rs b/sdk/program/src/vote/state/vote_state_versions.rs index 58d63d15def379..f720e36fb87770 100644 --- a/sdk/program/src/vote/state/vote_state_versions.rs +++ b/sdk/program/src/vote/state/vote_state_versions.rs @@ -73,9 +73,9 @@ impl VoteStateVersions { vote_state.authorized_voter == Pubkey::default() } - VoteStateVersions::V1_14_11(vote_state) => vote_state.authorized_voters.is_empty(), + VoteStateVersions::V1_14_11(vote_state) => vote_state.is_uninitialized(), - VoteStateVersions::Current(vote_state) => vote_state.authorized_voters.is_empty(), + VoteStateVersions::Current(vote_state) => vote_state.is_uninitialized(), } } diff --git a/vote/src/vote_account.rs b/vote/src/vote_account.rs index 9e26fd751c45ab..53d9e71cba9f3a 100644 --- a/vote/src/vote_account.rs +++ b/vote/src/vote_account.rs @@ -65,13 +65,10 @@ impl VoteAccount { } pub fn vote_state(&self) -> Result<&VoteState, &Error> { - // VoteState::deserialize_with_bincode deserializes a VoteStateVersions and then - // calls VoteStateVersions::convert_to_current. + // VoteState::deserialize deserializes a VoteStateVersions directly into VoteState self.0 .vote_state - .get_or_init(|| { - VoteState::deserialize_with_bincode(self.0.account.data()).map_err(Error::from) - }) + .get_or_init(|| VoteState::deserialize(self.0.account.data()).map_err(Error::from)) .as_ref() }