diff --git a/programs/vote/src/vote_instruction.rs b/programs/vote/src/vote_instruction.rs index 9b4c46dd5d..2e114cbfa7 100644 --- a/programs/vote/src/vote_instruction.rs +++ b/programs/vote/src/vote_instruction.rs @@ -161,18 +161,14 @@ pub enum VoteInstruction { /// /// # Account references /// 0. `[Write]` Vote account to vote with - /// 1. `[]` Slot hashes sysvar - /// 2. `[]` Clock sysvar - /// 3. `[SIGNER]` Vote authority + /// 1. `[SIGNER]` Vote authority UpdateVoteState(VoteStateUpdate), /// Update the onchain vote state for the signer along with a switching proof. /// /// # Account references /// 0. `[Write]` Vote account to vote with - /// 1. `[]` Slot hashes sysvar - /// 2. `[]` Clock sysvar - /// 3. `[SIGNER]` Vote authority + /// 1. `[SIGNER]` Vote authority UpdateVoteStateSwitch(VoteStateUpdate, Hash), } @@ -338,8 +334,6 @@ pub fn update_vote_state( ) -> Instruction { let account_metas = vec![ AccountMeta::new(*vote_pubkey, false), - AccountMeta::new_readonly(sysvar::slot_hashes::id(), false), - AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(*authorized_voter_pubkey, true), ]; @@ -358,8 +352,6 @@ pub fn update_vote_state_switch( ) -> Instruction { let account_metas = vec![ AccountMeta::new(*vote_pubkey, false), - AccountMeta::new_readonly(sysvar::slot_hashes::id(), false), - AccountMeta::new_readonly(sysvar::clock::id(), false), AccountMeta::new_readonly(*authorized_voter_pubkey, true), ]; @@ -465,20 +457,23 @@ pub fn process_instruction( } VoteInstruction::UpdateVoteState(vote_state_update) | VoteInstruction::UpdateVoteStateSwitch(vote_state_update, _) => { - inc_new_counter_info!("vote-state-native", 1); - vote_state::process_vote_state_update( - me, - &from_keyed_account::(keyed_account_at_index( - keyed_accounts, - first_instruction_account + 1, - )?)?, - &from_keyed_account::(keyed_account_at_index( - keyed_accounts, - first_instruction_account + 2, - )?)?, - &vote_state_update, - &signers, - ) + if invoke_context + .feature_set + .is_active(&feature_set::allow_votes_to_directly_update_vote_state::id()) + { + inc_new_counter_info!("vote-state-native", 1); + let slot_hashes: SlotHashes = + invoke_context.get_sysvar(&sysvar::slot_hashes::id())?; + vote_state::process_vote_state_update( + me, + slot_hashes.slot_hashes(), + &invoke_context.get_sysvar(&sysvar::clock::id())?, + vote_state_update, + &signers, + ) + } else { + Err(InstructionError::InvalidInstructionData) + } } VoteInstruction::Withdraw(lamports) => { let to = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?; @@ -585,12 +580,19 @@ mod tests { let rent = Rent::default(); let rent_sysvar = (sysvar::rent::id(), bincode::serialize(&rent).unwrap()); + let clock = Clock::default(); + let clock_sysvar = (sysvar::clock::id(), bincode::serialize(&clock).unwrap()); + let slot_hashes = SlotHashes::default(); + let slot_hashes_sysvar = ( + sysvar::slot_hashes::id(), + bincode::serialize(&slot_hashes).unwrap(), + ); solana_program_runtime::invoke_context::mock_process_instruction_with_sysvars( &id(), Vec::new(), &instruction.data, &keyed_accounts, - &[rent_sysvar], + &[rent_sysvar, clock_sysvar, slot_hashes_sysvar], super::process_instruction, ) } diff --git a/programs/vote/src/vote_state/mod.rs b/programs/vote/src/vote_state/mod.rs index eab08f0ac7..2435cc47fe 100644 --- a/programs/vote/src/vote_state/mod.rs +++ b/programs/vote/src/vote_state/mod.rs @@ -38,7 +38,7 @@ pub const INITIAL_LOCKOUT: usize = 2; // Maximum number of credits history to keep around pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64; -// Offset of VoteState::pri : Clone + Debug {or_voters, for determining initialization status without deserialization +// Offset of VoteState::prior_voters, for determining initialization status without deserialization const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 82; // VoteTransactionClone hack is done so that we can derive clone on the tower that uses the @@ -1156,14 +1156,14 @@ pub fn process_vote_state_update( vote_account: &KeyedAccount, slot_hashes: &[SlotHash], clock: &Clock, - vote_state_update: &VoteStateUpdate, + vote_state_update: VoteStateUpdate, signers: &HashSet, ) -> Result<(), InstructionError> { let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?; - vote_state.check_slots_are_valid(vote_state_update, slot_hashes)?; + vote_state.check_slots_are_valid(&vote_state_update, slot_hashes)?; vote_state.process_new_vote_state( - vote_state_update.lockouts.clone(), + vote_state_update.lockouts, vote_state_update.root, vote_state_update.timestamp, clock.epoch, diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 00541c3566..24bf8c529d 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -275,6 +275,10 @@ pub mod evict_invalid_stakes_cache_entries { solana_sdk::declare_id!("EMX9Q7TVFAmQ9V1CggAkhMzhXSg8ECp7fHrWQX2G1chf"); } +pub mod allow_votes_to_directly_update_vote_state { + solana_sdk::declare_id!("Ff8b1fBeB86q8cjq47ZhsQLgv5EkHu3G1C99zjUfAzrq"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -338,6 +342,7 @@ lazy_static! { (fixed_memcpy_nonoverlapping_check::id(), "use correct check for nonoverlapping regions in memcpy syscall"), (reject_non_rent_exempt_vote_withdraws::id(), "fail vote withdraw instructions which leave the account non-rent-exempt"), (evict_invalid_stakes_cache_entries::id(), "evict invalid stakes cache entries on epoch boundaries"), + (allow_votes_to_directly_update_vote_state::id(), "enable direct vote state update"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter()