solana_vote_program/
vote_processor.rs

1//! Vote program processor
2
3use {
4    crate::{vote_error::VoteError, vote_state},
5    log::*,
6    solana_program::vote::{instruction::VoteInstruction, program::id, state::VoteAuthorize},
7    solana_program_runtime::{
8        invoke_context::InvokeContext, sysvar_cache::get_sysvar_with_account_check,
9    },
10    solana_sdk::{
11        feature_set,
12        instruction::InstructionError,
13        program_utils::limited_deserialize,
14        pubkey::Pubkey,
15        transaction_context::{
16            BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
17        },
18    },
19    std::collections::HashSet,
20};
21
22fn process_authorize_with_seed_instruction(
23    invoke_context: &InvokeContext,
24    instruction_context: &InstructionContext,
25    transaction_context: &TransactionContext,
26    vote_account: &mut BorrowedAccount,
27    new_authority: &Pubkey,
28    authorization_type: VoteAuthorize,
29    current_authority_derived_key_owner: &Pubkey,
30    current_authority_derived_key_seed: &str,
31) -> Result<(), InstructionError> {
32    if !invoke_context
33        .feature_set
34        .is_active(&feature_set::vote_authorize_with_seed::id())
35    {
36        return Err(InstructionError::InvalidInstructionData);
37    }
38    let clock = get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?;
39    let mut expected_authority_keys: HashSet<Pubkey> = HashSet::default();
40    if instruction_context.is_instruction_account_signer(2)? {
41        let base_pubkey = transaction_context.get_key_of_account_at_index(
42            instruction_context.get_index_of_instruction_account_in_transaction(2)?,
43        )?;
44        expected_authority_keys.insert(Pubkey::create_with_seed(
45            base_pubkey,
46            current_authority_derived_key_seed,
47            current_authority_derived_key_owner,
48        )?);
49    };
50    vote_state::authorize(
51        vote_account,
52        new_authority,
53        authorization_type,
54        &expected_authority_keys,
55        &clock,
56        &invoke_context.feature_set,
57    )
58}
59
60pub fn process_instruction(
61    _first_instruction_account: IndexOfAccount,
62    invoke_context: &mut InvokeContext,
63) -> Result<(), InstructionError> {
64    let transaction_context = &invoke_context.transaction_context;
65    let instruction_context = transaction_context.get_current_instruction_context()?;
66    let data = instruction_context.get_instruction_data();
67
68    trace!("process_instruction: {:?}", data);
69
70    let mut me = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
71    if *me.get_owner() != id() {
72        return Err(InstructionError::InvalidAccountOwner);
73    }
74
75    let signers = instruction_context.get_signers(transaction_context)?;
76    match limited_deserialize(data)? {
77        VoteInstruction::InitializeAccount(vote_init) => {
78            let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?;
79            if !rent.is_exempt(me.get_lamports(), me.get_data().len()) {
80                return Err(InstructionError::InsufficientFunds);
81            }
82            let clock =
83                get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?;
84            vote_state::initialize_account(&mut me, &vote_init, &signers, &clock)
85        }
86        VoteInstruction::Authorize(voter_pubkey, vote_authorize) => {
87            let clock =
88                get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?;
89            vote_state::authorize(
90                &mut me,
91                &voter_pubkey,
92                vote_authorize,
93                &signers,
94                &clock,
95                &invoke_context.feature_set,
96            )
97        }
98        VoteInstruction::AuthorizeWithSeed(args) => {
99            instruction_context.check_number_of_instruction_accounts(3)?;
100            process_authorize_with_seed_instruction(
101                invoke_context,
102                instruction_context,
103                transaction_context,
104                &mut me,
105                &args.new_authority,
106                args.authorization_type,
107                &args.current_authority_derived_key_owner,
108                args.current_authority_derived_key_seed.as_str(),
109            )
110        }
111        VoteInstruction::AuthorizeCheckedWithSeed(args) => {
112            instruction_context.check_number_of_instruction_accounts(4)?;
113            let new_authority = transaction_context.get_key_of_account_at_index(
114                instruction_context.get_index_of_instruction_account_in_transaction(3)?,
115            )?;
116            if !instruction_context.is_instruction_account_signer(3)? {
117                return Err(InstructionError::MissingRequiredSignature);
118            }
119            process_authorize_with_seed_instruction(
120                invoke_context,
121                instruction_context,
122                transaction_context,
123                &mut me,
124                new_authority,
125                args.authorization_type,
126                &args.current_authority_derived_key_owner,
127                args.current_authority_derived_key_seed.as_str(),
128            )
129        }
130        VoteInstruction::UpdateValidatorIdentity => {
131            instruction_context.check_number_of_instruction_accounts(2)?;
132            let node_pubkey = transaction_context.get_key_of_account_at_index(
133                instruction_context.get_index_of_instruction_account_in_transaction(1)?,
134            )?;
135            vote_state::update_validator_identity(&mut me, node_pubkey, &signers)
136        }
137        VoteInstruction::UpdateCommission(commission) => {
138            if invoke_context.feature_set.is_active(
139                &feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id(),
140            ) {
141                let sysvar_cache = invoke_context.get_sysvar_cache();
142                let epoch_schedule = sysvar_cache.get_epoch_schedule()?;
143                let clock = sysvar_cache.get_clock()?;
144                if !vote_state::is_commission_update_allowed(clock.slot, &epoch_schedule) {
145                    return Err(VoteError::CommissionUpdateTooLate.into());
146                }
147            }
148            vote_state::update_commission(&mut me, commission, &signers)
149        }
150        VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => {
151            let slot_hashes =
152                get_sysvar_with_account_check::slot_hashes(invoke_context, instruction_context, 1)?;
153            let clock =
154                get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?;
155            vote_state::process_vote_with_account(
156                &mut me,
157                &slot_hashes,
158                &clock,
159                &vote,
160                &signers,
161                &invoke_context.feature_set,
162            )
163        }
164        VoteInstruction::UpdateVoteState(vote_state_update)
165        | VoteInstruction::UpdateVoteStateSwitch(vote_state_update, _) => {
166            if invoke_context
167                .feature_set
168                .is_active(&feature_set::allow_votes_to_directly_update_vote_state::id())
169            {
170                let sysvar_cache = invoke_context.get_sysvar_cache();
171                let slot_hashes = sysvar_cache.get_slot_hashes()?;
172                let clock = sysvar_cache.get_clock()?;
173                vote_state::process_vote_state_update(
174                    &mut me,
175                    slot_hashes.slot_hashes(),
176                    &clock,
177                    vote_state_update,
178                    &signers,
179                    &invoke_context.feature_set,
180                )
181            } else {
182                Err(InstructionError::InvalidInstructionData)
183            }
184        }
185        VoteInstruction::CompactUpdateVoteState(vote_state_update)
186        | VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
187            if invoke_context
188                .feature_set
189                .is_active(&feature_set::allow_votes_to_directly_update_vote_state::id())
190                && invoke_context
191                    .feature_set
192                    .is_active(&feature_set::compact_vote_state_updates::id())
193            {
194                let sysvar_cache = invoke_context.get_sysvar_cache();
195                let slot_hashes = sysvar_cache.get_slot_hashes()?;
196                let clock = sysvar_cache.get_clock()?;
197                vote_state::process_vote_state_update(
198                    &mut me,
199                    slot_hashes.slot_hashes(),
200                    &clock,
201                    vote_state_update,
202                    &signers,
203                    &invoke_context.feature_set,
204                )
205            } else {
206                Err(InstructionError::InvalidInstructionData)
207            }
208        }
209
210        VoteInstruction::Withdraw(lamports) => {
211            instruction_context.check_number_of_instruction_accounts(2)?;
212            let rent_sysvar = invoke_context.get_sysvar_cache().get_rent()?;
213
214            let clock_if_feature_active = if invoke_context
215                .feature_set
216                .is_active(&feature_set::reject_vote_account_close_unless_zero_credit_epoch::id())
217            {
218                Some(invoke_context.get_sysvar_cache().get_clock()?)
219            } else {
220                None
221            };
222
223            drop(me);
224            vote_state::withdraw(
225                transaction_context,
226                instruction_context,
227                0,
228                lamports,
229                1,
230                &signers,
231                &rent_sysvar,
232                clock_if_feature_active.as_deref(),
233            )
234        }
235        VoteInstruction::AuthorizeChecked(vote_authorize) => {
236            if invoke_context
237                .feature_set
238                .is_active(&feature_set::vote_stake_checked_instructions::id())
239            {
240                instruction_context.check_number_of_instruction_accounts(4)?;
241                let voter_pubkey = transaction_context.get_key_of_account_at_index(
242                    instruction_context.get_index_of_instruction_account_in_transaction(3)?,
243                )?;
244                if !instruction_context.is_instruction_account_signer(3)? {
245                    return Err(InstructionError::MissingRequiredSignature);
246                }
247                let clock =
248                    get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?;
249                vote_state::authorize(
250                    &mut me,
251                    voter_pubkey,
252                    vote_authorize,
253                    &signers,
254                    &clock,
255                    &invoke_context.feature_set,
256                )
257            } else {
258                Err(InstructionError::InvalidInstructionData)
259            }
260        }
261    }
262}
263
264#[cfg(test)]
265mod tests {
266    use {
267        super::*,
268        crate::{
269            vote_error::VoteError,
270            vote_instruction::{
271                authorize, authorize_checked, create_account, update_commission,
272                update_validator_identity, update_vote_state, update_vote_state_switch, vote,
273                vote_switch, withdraw, VoteInstruction,
274            },
275            vote_state::{
276                self, Lockout, Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs,
277                VoteAuthorizeWithSeedArgs, VoteInit, VoteState, VoteStateUpdate, VoteStateVersions,
278            },
279        },
280        bincode::serialize,
281        solana_program_runtime::invoke_context::mock_process_instruction,
282        solana_sdk::{
283            account::{self, Account, AccountSharedData, ReadableAccount},
284            account_utils::StateMut,
285            feature_set::FeatureSet,
286            hash::Hash,
287            instruction::{AccountMeta, Instruction},
288            pubkey::Pubkey,
289            sysvar::{
290                self, clock::Clock, epoch_schedule::EpochSchedule, rent::Rent,
291                slot_hashes::SlotHashes,
292            },
293        },
294        std::{collections::HashSet, str::FromStr},
295    };
296
297    struct VoteAccountTestFixtureWithAuthorities {
298        vote_account: AccountSharedData,
299        vote_pubkey: Pubkey,
300        voter_base_key: Pubkey,
301        voter_owner: Pubkey,
302        voter_seed: String,
303        withdrawer_base_key: Pubkey,
304        withdrawer_owner: Pubkey,
305        withdrawer_seed: String,
306    }
307
308    fn create_default_account() -> AccountSharedData {
309        AccountSharedData::new(0, 0, &Pubkey::new_unique())
310    }
311
312    fn process_instruction(
313        instruction_data: &[u8],
314        transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
315        instruction_accounts: Vec<AccountMeta>,
316        expected_result: Result<(), InstructionError>,
317    ) -> Vec<AccountSharedData> {
318        mock_process_instruction(
319            &id(),
320            Vec::new(),
321            instruction_data,
322            transaction_accounts,
323            instruction_accounts,
324            None,
325            None,
326            expected_result,
327            super::process_instruction,
328        )
329    }
330
331    fn process_instruction_disabled_features(
332        instruction_data: &[u8],
333        transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
334        instruction_accounts: Vec<AccountMeta>,
335        expected_result: Result<(), InstructionError>,
336    ) -> Vec<AccountSharedData> {
337        mock_process_instruction(
338            &id(),
339            Vec::new(),
340            instruction_data,
341            transaction_accounts,
342            instruction_accounts,
343            None,
344            Some(std::sync::Arc::new(FeatureSet::default())),
345            expected_result,
346            super::process_instruction,
347        )
348    }
349
350    fn process_instruction_as_one_arg(
351        instruction: &Instruction,
352        expected_result: Result<(), InstructionError>,
353    ) -> Vec<AccountSharedData> {
354        let mut pubkeys: HashSet<Pubkey> = instruction
355            .accounts
356            .iter()
357            .map(|meta| meta.pubkey)
358            .collect();
359        pubkeys.insert(sysvar::clock::id());
360        pubkeys.insert(sysvar::epoch_schedule::id());
361        pubkeys.insert(sysvar::rent::id());
362        pubkeys.insert(sysvar::slot_hashes::id());
363        let transaction_accounts: Vec<_> = pubkeys
364            .iter()
365            .map(|pubkey| {
366                (
367                    *pubkey,
368                    if sysvar::clock::check_id(pubkey) {
369                        account::create_account_shared_data_for_test(&Clock::default())
370                    } else if sysvar::epoch_schedule::check_id(pubkey) {
371                        account::create_account_shared_data_for_test(
372                            &EpochSchedule::without_warmup(),
373                        )
374                    } else if sysvar::slot_hashes::check_id(pubkey) {
375                        account::create_account_shared_data_for_test(&SlotHashes::default())
376                    } else if sysvar::rent::check_id(pubkey) {
377                        account::create_account_shared_data_for_test(&Rent::free())
378                    } else if *pubkey == invalid_vote_state_pubkey() {
379                        AccountSharedData::from(Account {
380                            owner: invalid_vote_state_pubkey(),
381                            ..Account::default()
382                        })
383                    } else {
384                        AccountSharedData::from(Account {
385                            owner: id(),
386                            ..Account::default()
387                        })
388                    },
389                )
390            })
391            .collect();
392        process_instruction(
393            &instruction.data,
394            transaction_accounts,
395            instruction.accounts.clone(),
396            expected_result,
397        )
398    }
399
400    fn invalid_vote_state_pubkey() -> Pubkey {
401        Pubkey::from_str("BadVote111111111111111111111111111111111111").unwrap()
402    }
403
404    fn create_default_rent_account() -> AccountSharedData {
405        account::create_account_shared_data_for_test(&Rent::free())
406    }
407
408    fn create_default_clock_account() -> AccountSharedData {
409        account::create_account_shared_data_for_test(&Clock::default())
410    }
411
412    fn create_test_account() -> (Pubkey, AccountSharedData) {
413        let rent = Rent::default();
414        let balance = VoteState::get_rent_exempt_reserve(&rent);
415        let vote_pubkey = solana_sdk::pubkey::new_rand();
416        (
417            vote_pubkey,
418            vote_state::create_account(&vote_pubkey, &solana_sdk::pubkey::new_rand(), 0, balance),
419        )
420    }
421
422    fn create_test_account_with_authorized() -> (Pubkey, Pubkey, Pubkey, AccountSharedData) {
423        let vote_pubkey = solana_sdk::pubkey::new_rand();
424        let authorized_voter = solana_sdk::pubkey::new_rand();
425        let authorized_withdrawer = solana_sdk::pubkey::new_rand();
426
427        (
428            vote_pubkey,
429            authorized_voter,
430            authorized_withdrawer,
431            vote_state::create_account_with_authorized(
432                &solana_sdk::pubkey::new_rand(),
433                &authorized_voter,
434                &authorized_withdrawer,
435                0,
436                100,
437            ),
438        )
439    }
440
441    fn create_test_account_with_authorized_from_seed() -> VoteAccountTestFixtureWithAuthorities {
442        let vote_pubkey = Pubkey::new_unique();
443        let voter_base_key = Pubkey::new_unique();
444        let voter_owner = Pubkey::new_unique();
445        let voter_seed = String::from("VOTER_SEED");
446        let withdrawer_base_key = Pubkey::new_unique();
447        let withdrawer_owner = Pubkey::new_unique();
448        let withdrawer_seed = String::from("WITHDRAWER_SEED");
449        let authorized_voter =
450            Pubkey::create_with_seed(&voter_base_key, voter_seed.as_str(), &voter_owner).unwrap();
451        let authorized_withdrawer = Pubkey::create_with_seed(
452            &withdrawer_base_key,
453            withdrawer_seed.as_str(),
454            &withdrawer_owner,
455        )
456        .unwrap();
457
458        VoteAccountTestFixtureWithAuthorities {
459            vote_account: vote_state::create_account_with_authorized(
460                &Pubkey::new_unique(),
461                &authorized_voter,
462                &authorized_withdrawer,
463                0,
464                100,
465            ),
466            vote_pubkey,
467            voter_base_key,
468            voter_owner,
469            voter_seed,
470            withdrawer_base_key,
471            withdrawer_owner,
472            withdrawer_seed,
473        }
474    }
475
476    fn create_test_account_with_epoch_credits(
477        credits_to_append: &[u64],
478    ) -> (Pubkey, AccountSharedData) {
479        let (vote_pubkey, vote_account) = create_test_account();
480        let vote_account_space = vote_account.data().len();
481
482        let mut vote_state = vote_state::from(&vote_account).unwrap();
483        vote_state.authorized_withdrawer = vote_pubkey;
484        vote_state.epoch_credits = Vec::new();
485
486        let mut current_epoch_credits: u64 = 0;
487        let mut previous_epoch_credits = 0;
488        for (epoch, credits) in credits_to_append.iter().enumerate() {
489            current_epoch_credits = current_epoch_credits.saturating_add(*credits);
490            vote_state.epoch_credits.push((
491                u64::try_from(epoch).unwrap(),
492                current_epoch_credits,
493                previous_epoch_credits,
494            ));
495            previous_epoch_credits = current_epoch_credits;
496        }
497
498        let lamports = vote_account.lamports();
499        let mut vote_account_with_epoch_credits =
500            AccountSharedData::new(lamports, vote_account_space, &id());
501        let versioned = VoteStateVersions::new_current(vote_state);
502        vote_state::to(&versioned, &mut vote_account_with_epoch_credits);
503
504        (vote_pubkey, vote_account_with_epoch_credits)
505    }
506
507    #[test]
508    fn test_vote_process_instruction_decode_bail() {
509        process_instruction(
510            &[],
511            Vec::new(),
512            Vec::new(),
513            Err(InstructionError::NotEnoughAccountKeys),
514        );
515    }
516
517    #[test]
518    fn test_initialize_vote_account() {
519        let vote_pubkey = solana_sdk::pubkey::new_rand();
520        let vote_account = AccountSharedData::new(100, VoteState::size_of(), &id());
521        let node_pubkey = solana_sdk::pubkey::new_rand();
522        let node_account = AccountSharedData::default();
523        let instruction_data = serialize(&VoteInstruction::InitializeAccount(VoteInit {
524            node_pubkey,
525            authorized_voter: vote_pubkey,
526            authorized_withdrawer: vote_pubkey,
527            commission: 0,
528        }))
529        .unwrap();
530        let mut instruction_accounts = vec![
531            AccountMeta {
532                pubkey: vote_pubkey,
533                is_signer: false,
534                is_writable: true,
535            },
536            AccountMeta {
537                pubkey: sysvar::rent::id(),
538                is_signer: false,
539                is_writable: false,
540            },
541            AccountMeta {
542                pubkey: sysvar::clock::id(),
543                is_signer: false,
544                is_writable: false,
545            },
546            AccountMeta {
547                pubkey: node_pubkey,
548                is_signer: true,
549                is_writable: false,
550            },
551        ];
552
553        // init should pass
554        let accounts = process_instruction(
555            &instruction_data,
556            vec![
557                (vote_pubkey, vote_account.clone()),
558                (sysvar::rent::id(), create_default_rent_account()),
559                (sysvar::clock::id(), create_default_clock_account()),
560                (node_pubkey, node_account.clone()),
561            ],
562            instruction_accounts.clone(),
563            Ok(()),
564        );
565
566        // reinit should fail
567        process_instruction(
568            &instruction_data,
569            vec![
570                (vote_pubkey, accounts[0].clone()),
571                (sysvar::rent::id(), create_default_rent_account()),
572                (sysvar::clock::id(), create_default_clock_account()),
573                (node_pubkey, accounts[3].clone()),
574            ],
575            instruction_accounts.clone(),
576            Err(InstructionError::AccountAlreadyInitialized),
577        );
578
579        // init should fail, account is too big
580        process_instruction(
581            &instruction_data,
582            vec![
583                (
584                    vote_pubkey,
585                    AccountSharedData::new(100, 2 * VoteState::size_of(), &id()),
586                ),
587                (sysvar::rent::id(), create_default_rent_account()),
588                (sysvar::clock::id(), create_default_clock_account()),
589                (node_pubkey, node_account.clone()),
590            ],
591            instruction_accounts.clone(),
592            Err(InstructionError::InvalidAccountData),
593        );
594
595        // init should fail, node_pubkey didn't sign the transaction
596        instruction_accounts[3].is_signer = false;
597        process_instruction(
598            &instruction_data,
599            vec![
600                (vote_pubkey, vote_account),
601                (sysvar::rent::id(), create_default_rent_account()),
602                (sysvar::clock::id(), create_default_clock_account()),
603                (node_pubkey, node_account),
604            ],
605            instruction_accounts,
606            Err(InstructionError::MissingRequiredSignature),
607        );
608    }
609
610    #[test]
611    fn test_vote_update_validator_identity() {
612        let (vote_pubkey, _authorized_voter, authorized_withdrawer, vote_account) =
613            create_test_account_with_authorized();
614        let node_pubkey = solana_sdk::pubkey::new_rand();
615        let instruction_data = serialize(&VoteInstruction::UpdateValidatorIdentity).unwrap();
616        let transaction_accounts = vec![
617            (vote_pubkey, vote_account),
618            (node_pubkey, AccountSharedData::default()),
619            (authorized_withdrawer, AccountSharedData::default()),
620        ];
621        let mut instruction_accounts = vec![
622            AccountMeta {
623                pubkey: vote_pubkey,
624                is_signer: false,
625                is_writable: true,
626            },
627            AccountMeta {
628                pubkey: node_pubkey,
629                is_signer: true,
630                is_writable: false,
631            },
632            AccountMeta {
633                pubkey: authorized_withdrawer,
634                is_signer: true,
635                is_writable: false,
636            },
637        ];
638
639        // should fail, node_pubkey didn't sign the transaction
640        instruction_accounts[1].is_signer = false;
641        let accounts = process_instruction(
642            &instruction_data,
643            transaction_accounts.clone(),
644            instruction_accounts.clone(),
645            Err(InstructionError::MissingRequiredSignature),
646        );
647        instruction_accounts[1].is_signer = true;
648        let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&accounts[0])
649            .unwrap()
650            .convert_to_current();
651        assert_ne!(vote_state.node_pubkey, node_pubkey);
652
653        // should fail, authorized_withdrawer didn't sign the transaction
654        instruction_accounts[2].is_signer = false;
655        let accounts = process_instruction(
656            &instruction_data,
657            transaction_accounts.clone(),
658            instruction_accounts.clone(),
659            Err(InstructionError::MissingRequiredSignature),
660        );
661        instruction_accounts[2].is_signer = true;
662        let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&accounts[0])
663            .unwrap()
664            .convert_to_current();
665        assert_ne!(vote_state.node_pubkey, node_pubkey);
666
667        // should pass
668        let accounts = process_instruction(
669            &instruction_data,
670            transaction_accounts,
671            instruction_accounts,
672            Ok(()),
673        );
674        let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&accounts[0])
675            .unwrap()
676            .convert_to_current();
677        assert_eq!(vote_state.node_pubkey, node_pubkey);
678    }
679
680    #[test]
681    fn test_vote_update_commission() {
682        let (vote_pubkey, _authorized_voter, authorized_withdrawer, vote_account) =
683            create_test_account_with_authorized();
684        let instruction_data = serialize(&VoteInstruction::UpdateCommission(42)).unwrap();
685        let transaction_accounts = vec![
686            (vote_pubkey, vote_account),
687            (authorized_withdrawer, AccountSharedData::default()),
688            // Add the sysvar accounts so they're in the cache for mock processing
689            (
690                sysvar::clock::id(),
691                account::create_account_shared_data_for_test(&Clock::default()),
692            ),
693            (
694                sysvar::epoch_schedule::id(),
695                account::create_account_shared_data_for_test(&EpochSchedule::without_warmup()),
696            ),
697        ];
698        let mut instruction_accounts = vec![
699            AccountMeta {
700                pubkey: vote_pubkey,
701                is_signer: false,
702                is_writable: true,
703            },
704            AccountMeta {
705                pubkey: authorized_withdrawer,
706                is_signer: true,
707                is_writable: false,
708            },
709        ];
710
711        // should pass
712        let accounts = process_instruction(
713            &serialize(&VoteInstruction::UpdateCommission(u8::MAX)).unwrap(),
714            transaction_accounts.clone(),
715            instruction_accounts.clone(),
716            Ok(()),
717        );
718        let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&accounts[0])
719            .unwrap()
720            .convert_to_current();
721        assert_eq!(vote_state.commission, u8::MAX);
722
723        // should pass
724        let accounts = process_instruction(
725            &instruction_data,
726            transaction_accounts.clone(),
727            instruction_accounts.clone(),
728            Ok(()),
729        );
730        let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&accounts[0])
731            .unwrap()
732            .convert_to_current();
733        assert_eq!(vote_state.commission, 42);
734
735        // should fail, authorized_withdrawer didn't sign the transaction
736        instruction_accounts[1].is_signer = false;
737        let accounts = process_instruction(
738            &instruction_data,
739            transaction_accounts,
740            instruction_accounts,
741            Err(InstructionError::MissingRequiredSignature),
742        );
743        let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&accounts[0])
744            .unwrap()
745            .convert_to_current();
746        assert_eq!(vote_state.commission, 0);
747    }
748
749    #[test]
750    fn test_vote_signature() {
751        let (vote_pubkey, vote_account) = create_test_account();
752        let vote = Vote::new(vec![1], Hash::default());
753        let slot_hashes = SlotHashes::new(&[(*vote.slots.last().unwrap(), vote.hash)]);
754        let slot_hashes_account = account::create_account_shared_data_for_test(&slot_hashes);
755        let instruction_data = serialize(&VoteInstruction::Vote(vote.clone())).unwrap();
756        let mut transaction_accounts = vec![
757            (vote_pubkey, vote_account),
758            (sysvar::slot_hashes::id(), slot_hashes_account.clone()),
759            (sysvar::clock::id(), create_default_clock_account()),
760        ];
761        let mut instruction_accounts = vec![
762            AccountMeta {
763                pubkey: vote_pubkey,
764                is_signer: true,
765                is_writable: true,
766            },
767            AccountMeta {
768                pubkey: sysvar::slot_hashes::id(),
769                is_signer: false,
770                is_writable: false,
771            },
772            AccountMeta {
773                pubkey: sysvar::clock::id(),
774                is_signer: false,
775                is_writable: false,
776            },
777        ];
778
779        // should fail, unsigned
780        instruction_accounts[0].is_signer = false;
781        process_instruction(
782            &instruction_data,
783            transaction_accounts.clone(),
784            instruction_accounts.clone(),
785            Err(InstructionError::MissingRequiredSignature),
786        );
787        instruction_accounts[0].is_signer = true;
788
789        // should pass
790        let accounts = process_instruction(
791            &instruction_data,
792            transaction_accounts.clone(),
793            instruction_accounts.clone(),
794            Ok(()),
795        );
796        let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&accounts[0])
797            .unwrap()
798            .convert_to_current();
799        assert_eq!(
800            vote_state.votes,
801            vec![Lockout::new(*vote.slots.last().unwrap())]
802        );
803        assert_eq!(vote_state.credits(), 0);
804
805        // should fail, wrong hash
806        transaction_accounts[1] = (
807            sysvar::slot_hashes::id(),
808            account::create_account_shared_data_for_test(&SlotHashes::new(&[(
809                *vote.slots.last().unwrap(),
810                solana_sdk::hash::hash(&[0u8]),
811            )])),
812        );
813        process_instruction(
814            &instruction_data,
815            transaction_accounts.clone(),
816            instruction_accounts.clone(),
817            Err(VoteError::SlotHashMismatch.into()),
818        );
819
820        // should fail, wrong slot
821        transaction_accounts[1] = (
822            sysvar::slot_hashes::id(),
823            account::create_account_shared_data_for_test(&SlotHashes::new(&[(0, vote.hash)])),
824        );
825        process_instruction(
826            &instruction_data,
827            transaction_accounts.clone(),
828            instruction_accounts.clone(),
829            Err(VoteError::SlotsMismatch.into()),
830        );
831
832        // should fail, empty slot_hashes
833        transaction_accounts[1] = (
834            sysvar::slot_hashes::id(),
835            account::create_account_shared_data_for_test(&SlotHashes::new(&[])),
836        );
837        process_instruction(
838            &instruction_data,
839            transaction_accounts.clone(),
840            instruction_accounts.clone(),
841            Err(VoteError::VoteTooOld.into()),
842        );
843        transaction_accounts[1] = (sysvar::slot_hashes::id(), slot_hashes_account);
844
845        // should fail, uninitialized
846        let vote_account = AccountSharedData::new(100, VoteState::size_of(), &id());
847        transaction_accounts[0] = (vote_pubkey, vote_account);
848        process_instruction(
849            &instruction_data,
850            transaction_accounts,
851            instruction_accounts,
852            Err(InstructionError::UninitializedAccount),
853        );
854    }
855
856    #[test]
857    fn test_authorize_voter() {
858        let (vote_pubkey, vote_account) = create_test_account();
859        let authorized_voter_pubkey = solana_sdk::pubkey::new_rand();
860        let clock = Clock {
861            epoch: 1,
862            leader_schedule_epoch: 2,
863            ..Clock::default()
864        };
865        let clock_account = account::create_account_shared_data_for_test(&clock);
866        let instruction_data = serialize(&VoteInstruction::Authorize(
867            authorized_voter_pubkey,
868            VoteAuthorize::Voter,
869        ))
870        .unwrap();
871        let mut transaction_accounts = vec![
872            (vote_pubkey, vote_account),
873            (sysvar::clock::id(), clock_account),
874            (authorized_voter_pubkey, AccountSharedData::default()),
875        ];
876        let mut instruction_accounts = vec![
877            AccountMeta {
878                pubkey: vote_pubkey,
879                is_signer: true,
880                is_writable: true,
881            },
882            AccountMeta {
883                pubkey: sysvar::clock::id(),
884                is_signer: false,
885                is_writable: false,
886            },
887        ];
888
889        // should fail, unsigned
890        instruction_accounts[0].is_signer = false;
891        process_instruction(
892            &instruction_data,
893            transaction_accounts.clone(),
894            instruction_accounts.clone(),
895            Err(InstructionError::MissingRequiredSignature),
896        );
897        instruction_accounts[0].is_signer = true;
898
899        // should pass
900        let accounts = process_instruction(
901            &instruction_data,
902            transaction_accounts.clone(),
903            instruction_accounts.clone(),
904            Ok(()),
905        );
906
907        // should fail, already set an authorized voter earlier for leader_schedule_epoch == 2
908        transaction_accounts[0] = (vote_pubkey, accounts[0].clone());
909        process_instruction(
910            &instruction_data,
911            transaction_accounts.clone(),
912            instruction_accounts.clone(),
913            Err(VoteError::TooSoonToReauthorize.into()),
914        );
915
916        // should pass, verify authorized_voter_pubkey can authorize authorized_voter_pubkey ;)
917        instruction_accounts[0].is_signer = false;
918        instruction_accounts.push(AccountMeta {
919            pubkey: authorized_voter_pubkey,
920            is_signer: true,
921            is_writable: false,
922        });
923        let clock = Clock {
924            // The authorized voter was set when leader_schedule_epoch == 2, so will
925            // take effect when epoch == 3
926            epoch: 3,
927            leader_schedule_epoch: 4,
928            ..Clock::default()
929        };
930        let clock_account = account::create_account_shared_data_for_test(&clock);
931        transaction_accounts[1] = (sysvar::clock::id(), clock_account);
932        process_instruction(
933            &instruction_data,
934            transaction_accounts.clone(),
935            instruction_accounts.clone(),
936            Ok(()),
937        );
938        instruction_accounts[0].is_signer = true;
939        instruction_accounts.pop();
940
941        // should fail, not signed by authorized voter
942        let vote = Vote::new(vec![1], Hash::default());
943        let slot_hashes = SlotHashes::new(&[(*vote.slots.last().unwrap(), vote.hash)]);
944        let slot_hashes_account = account::create_account_shared_data_for_test(&slot_hashes);
945        let instruction_data = serialize(&VoteInstruction::Vote(vote)).unwrap();
946        transaction_accounts.push((sysvar::slot_hashes::id(), slot_hashes_account));
947        instruction_accounts.insert(
948            1,
949            AccountMeta {
950                pubkey: sysvar::slot_hashes::id(),
951                is_signer: false,
952                is_writable: false,
953            },
954        );
955        process_instruction(
956            &instruction_data,
957            transaction_accounts.clone(),
958            instruction_accounts.clone(),
959            Err(InstructionError::MissingRequiredSignature),
960        );
961
962        // should pass, signed by authorized voter
963        instruction_accounts.push(AccountMeta {
964            pubkey: authorized_voter_pubkey,
965            is_signer: true,
966            is_writable: false,
967        });
968        process_instruction(
969            &instruction_data,
970            transaction_accounts,
971            instruction_accounts,
972            Ok(()),
973        );
974    }
975
976    #[test]
977    fn test_authorize_withdrawer() {
978        let (vote_pubkey, vote_account) = create_test_account();
979        let authorized_withdrawer_pubkey = solana_sdk::pubkey::new_rand();
980        let instruction_data = serialize(&VoteInstruction::Authorize(
981            authorized_withdrawer_pubkey,
982            VoteAuthorize::Withdrawer,
983        ))
984        .unwrap();
985        let mut transaction_accounts = vec![
986            (vote_pubkey, vote_account),
987            (sysvar::clock::id(), create_default_clock_account()),
988            (authorized_withdrawer_pubkey, AccountSharedData::default()),
989        ];
990        let mut instruction_accounts = vec![
991            AccountMeta {
992                pubkey: vote_pubkey,
993                is_signer: true,
994                is_writable: true,
995            },
996            AccountMeta {
997                pubkey: sysvar::clock::id(),
998                is_signer: false,
999                is_writable: false,
1000            },
1001        ];
1002
1003        // should fail, unsigned
1004        instruction_accounts[0].is_signer = false;
1005        process_instruction(
1006            &instruction_data,
1007            transaction_accounts.clone(),
1008            instruction_accounts.clone(),
1009            Err(InstructionError::MissingRequiredSignature),
1010        );
1011        instruction_accounts[0].is_signer = true;
1012
1013        // should pass
1014        let accounts = process_instruction(
1015            &instruction_data,
1016            transaction_accounts.clone(),
1017            instruction_accounts.clone(),
1018            Ok(()),
1019        );
1020
1021        // should pass, verify authorized_withdrawer can authorize authorized_withdrawer ;)
1022        instruction_accounts[0].is_signer = false;
1023        instruction_accounts.push(AccountMeta {
1024            pubkey: authorized_withdrawer_pubkey,
1025            is_signer: true,
1026            is_writable: false,
1027        });
1028        transaction_accounts[0] = (vote_pubkey, accounts[0].clone());
1029        process_instruction(
1030            &instruction_data,
1031            transaction_accounts.clone(),
1032            instruction_accounts.clone(),
1033            Ok(()),
1034        );
1035
1036        // should pass, verify authorized_withdrawer can authorize a new authorized_voter
1037        let authorized_voter_pubkey = solana_sdk::pubkey::new_rand();
1038        transaction_accounts.push((authorized_voter_pubkey, AccountSharedData::default()));
1039        let instruction_data = serialize(&VoteInstruction::Authorize(
1040            authorized_voter_pubkey,
1041            VoteAuthorize::Voter,
1042        ))
1043        .unwrap();
1044        process_instruction(
1045            &instruction_data,
1046            transaction_accounts.clone(),
1047            instruction_accounts.clone(),
1048            Ok(()),
1049        );
1050
1051        // should fail, if vote_withdraw_authority_may_change_authorized_voter is disabled
1052        process_instruction_disabled_features(
1053            &instruction_data,
1054            transaction_accounts,
1055            instruction_accounts,
1056            Err(InstructionError::MissingRequiredSignature),
1057        );
1058    }
1059
1060    #[test]
1061    fn test_vote_withdraw() {
1062        let (vote_pubkey, vote_account) = create_test_account();
1063        let lamports = vote_account.lamports();
1064        let authorized_withdrawer_pubkey = solana_sdk::pubkey::new_rand();
1065        let mut transaction_accounts = vec![
1066            (vote_pubkey, vote_account.clone()),
1067            (sysvar::clock::id(), create_default_clock_account()),
1068            (sysvar::rent::id(), create_default_rent_account()),
1069            (authorized_withdrawer_pubkey, AccountSharedData::default()),
1070        ];
1071        let mut instruction_accounts = vec![
1072            AccountMeta {
1073                pubkey: vote_pubkey,
1074                is_signer: true,
1075                is_writable: true,
1076            },
1077            AccountMeta {
1078                pubkey: sysvar::clock::id(),
1079                is_signer: false,
1080                is_writable: false,
1081            },
1082        ];
1083
1084        // should pass, withdraw using authorized_withdrawer to authorized_withdrawer's account
1085        let accounts = process_instruction(
1086            &serialize(&VoteInstruction::Authorize(
1087                authorized_withdrawer_pubkey,
1088                VoteAuthorize::Withdrawer,
1089            ))
1090            .unwrap(),
1091            transaction_accounts.clone(),
1092            instruction_accounts.clone(),
1093            Ok(()),
1094        );
1095        instruction_accounts[0].is_signer = false;
1096        instruction_accounts[1] = AccountMeta {
1097            pubkey: authorized_withdrawer_pubkey,
1098            is_signer: true,
1099            is_writable: true,
1100        };
1101        transaction_accounts[0] = (vote_pubkey, accounts[0].clone());
1102        let accounts = process_instruction(
1103            &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
1104            transaction_accounts.clone(),
1105            instruction_accounts.clone(),
1106            Ok(()),
1107        );
1108        assert_eq!(accounts[0].lamports(), 0);
1109        assert_eq!(accounts[3].lamports(), lamports);
1110        let post_state: VoteStateVersions = accounts[0].state().unwrap();
1111        // State has been deinitialized since balance is zero
1112        assert!(post_state.is_uninitialized());
1113
1114        // should fail, unsigned
1115        transaction_accounts[0] = (vote_pubkey, vote_account);
1116        process_instruction(
1117            &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
1118            transaction_accounts.clone(),
1119            instruction_accounts.clone(),
1120            Err(InstructionError::MissingRequiredSignature),
1121        );
1122        instruction_accounts[0].is_signer = true;
1123
1124        // should pass
1125        process_instruction(
1126            &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
1127            transaction_accounts.clone(),
1128            instruction_accounts.clone(),
1129            Ok(()),
1130        );
1131
1132        // should fail, insufficient funds
1133        process_instruction(
1134            &serialize(&VoteInstruction::Withdraw(lamports + 1)).unwrap(),
1135            transaction_accounts.clone(),
1136            instruction_accounts.clone(),
1137            Err(InstructionError::InsufficientFunds),
1138        );
1139
1140        // should pass, partial withdraw
1141        let withdraw_lamports = 42;
1142        let accounts = process_instruction(
1143            &serialize(&VoteInstruction::Withdraw(withdraw_lamports)).unwrap(),
1144            transaction_accounts,
1145            instruction_accounts,
1146            Ok(()),
1147        );
1148        assert_eq!(accounts[0].lamports(), lamports - withdraw_lamports);
1149        assert_eq!(accounts[3].lamports(), withdraw_lamports);
1150    }
1151
1152    #[test]
1153    fn test_vote_state_withdraw() {
1154        let authorized_withdrawer_pubkey = solana_sdk::pubkey::new_rand();
1155        let (vote_pubkey_1, vote_account_with_epoch_credits_1) =
1156            create_test_account_with_epoch_credits(&[2, 1]);
1157        let (vote_pubkey_2, vote_account_with_epoch_credits_2) =
1158            create_test_account_with_epoch_credits(&[2, 1, 3]);
1159        let clock = Clock {
1160            epoch: 3,
1161            ..Clock::default()
1162        };
1163        let clock_account = account::create_account_shared_data_for_test(&clock);
1164        let rent_sysvar = Rent::default();
1165        let minimum_balance = rent_sysvar
1166            .minimum_balance(vote_account_with_epoch_credits_1.data().len())
1167            .max(1);
1168        let lamports = vote_account_with_epoch_credits_1.lamports();
1169        let transaction_accounts = vec![
1170            (vote_pubkey_1, vote_account_with_epoch_credits_1),
1171            (vote_pubkey_2, vote_account_with_epoch_credits_2),
1172            (sysvar::clock::id(), clock_account),
1173            (
1174                sysvar::rent::id(),
1175                account::create_account_shared_data_for_test(&rent_sysvar),
1176            ),
1177            (authorized_withdrawer_pubkey, AccountSharedData::default()),
1178        ];
1179        let mut instruction_accounts = vec![
1180            AccountMeta {
1181                pubkey: vote_pubkey_1,
1182                is_signer: true,
1183                is_writable: true,
1184            },
1185            AccountMeta {
1186                pubkey: authorized_withdrawer_pubkey,
1187                is_signer: false,
1188                is_writable: true,
1189            },
1190        ];
1191
1192        // non rent exempt withdraw, with 0 credit epoch
1193        instruction_accounts[0].pubkey = vote_pubkey_1;
1194        process_instruction(
1195            &serialize(&VoteInstruction::Withdraw(lamports - minimum_balance + 1)).unwrap(),
1196            transaction_accounts.clone(),
1197            instruction_accounts.clone(),
1198            Err(InstructionError::InsufficientFunds),
1199        );
1200
1201        // non rent exempt withdraw, without 0 credit epoch
1202        instruction_accounts[0].pubkey = vote_pubkey_2;
1203        process_instruction(
1204            &serialize(&VoteInstruction::Withdraw(lamports - minimum_balance + 1)).unwrap(),
1205            transaction_accounts.clone(),
1206            instruction_accounts.clone(),
1207            Err(InstructionError::InsufficientFunds),
1208        );
1209
1210        // full withdraw, with 0 credit epoch
1211        instruction_accounts[0].pubkey = vote_pubkey_1;
1212        process_instruction(
1213            &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
1214            transaction_accounts.clone(),
1215            instruction_accounts.clone(),
1216            Ok(()),
1217        );
1218
1219        // full withdraw, without 0 credit epoch
1220        instruction_accounts[0].pubkey = vote_pubkey_2;
1221        process_instruction(
1222            &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
1223            transaction_accounts.clone(),
1224            instruction_accounts.clone(),
1225            Err(VoteError::ActiveVoteAccountClose.into()),
1226        );
1227
1228        // Following features disabled:
1229        // reject_vote_account_close_unless_zero_credit_epoch
1230
1231        // full withdraw, with 0 credit epoch
1232        instruction_accounts[0].pubkey = vote_pubkey_1;
1233        process_instruction_disabled_features(
1234            &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
1235            transaction_accounts.clone(),
1236            instruction_accounts.clone(),
1237            Ok(()),
1238        );
1239
1240        // full withdraw, without 0 credit epoch
1241        instruction_accounts[0].pubkey = vote_pubkey_2;
1242        process_instruction_disabled_features(
1243            &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
1244            transaction_accounts,
1245            instruction_accounts,
1246            Ok(()),
1247        );
1248    }
1249
1250    fn perform_authorize_with_seed_test(
1251        authorization_type: VoteAuthorize,
1252        vote_pubkey: Pubkey,
1253        vote_account: AccountSharedData,
1254        current_authority_base_key: Pubkey,
1255        current_authority_seed: String,
1256        current_authority_owner: Pubkey,
1257        new_authority_pubkey: Pubkey,
1258    ) {
1259        let clock = Clock {
1260            epoch: 1,
1261            leader_schedule_epoch: 2,
1262            ..Clock::default()
1263        };
1264        let clock_account = account::create_account_shared_data_for_test(&clock);
1265        let transaction_accounts = vec![
1266            (vote_pubkey, vote_account),
1267            (sysvar::clock::id(), clock_account),
1268            (current_authority_base_key, AccountSharedData::default()),
1269        ];
1270        let mut instruction_accounts = vec![
1271            AccountMeta {
1272                pubkey: vote_pubkey,
1273                is_signer: false,
1274                is_writable: true,
1275            },
1276            AccountMeta {
1277                pubkey: sysvar::clock::id(),
1278                is_signer: false,
1279                is_writable: false,
1280            },
1281            AccountMeta {
1282                pubkey: current_authority_base_key,
1283                is_signer: true,
1284                is_writable: false,
1285            },
1286        ];
1287
1288        // Can't change authority unless base key signs.
1289        instruction_accounts[2].is_signer = false;
1290        process_instruction(
1291            &serialize(&VoteInstruction::AuthorizeWithSeed(
1292                VoteAuthorizeWithSeedArgs {
1293                    authorization_type,
1294                    current_authority_derived_key_owner: current_authority_owner,
1295                    current_authority_derived_key_seed: current_authority_seed.clone(),
1296                    new_authority: new_authority_pubkey,
1297                },
1298            ))
1299            .unwrap(),
1300            transaction_accounts.clone(),
1301            instruction_accounts.clone(),
1302            Err(InstructionError::MissingRequiredSignature),
1303        );
1304        instruction_accounts[2].is_signer = true;
1305
1306        // Can't change authority if seed doesn't match.
1307        process_instruction(
1308            &serialize(&VoteInstruction::AuthorizeWithSeed(
1309                VoteAuthorizeWithSeedArgs {
1310                    authorization_type,
1311                    current_authority_derived_key_owner: current_authority_owner,
1312                    current_authority_derived_key_seed: String::from("WRONG_SEED"),
1313                    new_authority: new_authority_pubkey,
1314                },
1315            ))
1316            .unwrap(),
1317            transaction_accounts.clone(),
1318            instruction_accounts.clone(),
1319            Err(InstructionError::MissingRequiredSignature),
1320        );
1321
1322        // Can't change authority if owner doesn't match.
1323        process_instruction(
1324            &serialize(&VoteInstruction::AuthorizeWithSeed(
1325                VoteAuthorizeWithSeedArgs {
1326                    authorization_type,
1327                    current_authority_derived_key_owner: Pubkey::new_unique(), // Wrong owner.
1328                    current_authority_derived_key_seed: current_authority_seed.clone(),
1329                    new_authority: new_authority_pubkey,
1330                },
1331            ))
1332            .unwrap(),
1333            transaction_accounts.clone(),
1334            instruction_accounts.clone(),
1335            Err(InstructionError::MissingRequiredSignature),
1336        );
1337
1338        // Can change authority when base key signs for related derived key.
1339        process_instruction(
1340            &serialize(&VoteInstruction::AuthorizeWithSeed(
1341                VoteAuthorizeWithSeedArgs {
1342                    authorization_type,
1343                    current_authority_derived_key_owner: current_authority_owner,
1344                    current_authority_derived_key_seed: current_authority_seed.clone(),
1345                    new_authority: new_authority_pubkey,
1346                },
1347            ))
1348            .unwrap(),
1349            transaction_accounts.clone(),
1350            instruction_accounts.clone(),
1351            Ok(()),
1352        );
1353
1354        // Should fail when the `vote_authorize_with_seed` feature is disabled
1355        process_instruction_disabled_features(
1356            &serialize(&VoteInstruction::AuthorizeWithSeed(
1357                VoteAuthorizeWithSeedArgs {
1358                    authorization_type,
1359                    current_authority_derived_key_owner: current_authority_owner,
1360                    current_authority_derived_key_seed: current_authority_seed,
1361                    new_authority: new_authority_pubkey,
1362                },
1363            ))
1364            .unwrap(),
1365            transaction_accounts,
1366            instruction_accounts,
1367            Err(InstructionError::InvalidInstructionData),
1368        );
1369    }
1370
1371    fn perform_authorize_checked_with_seed_test(
1372        authorization_type: VoteAuthorize,
1373        vote_pubkey: Pubkey,
1374        vote_account: AccountSharedData,
1375        current_authority_base_key: Pubkey,
1376        current_authority_seed: String,
1377        current_authority_owner: Pubkey,
1378        new_authority_pubkey: Pubkey,
1379    ) {
1380        let clock = Clock {
1381            epoch: 1,
1382            leader_schedule_epoch: 2,
1383            ..Clock::default()
1384        };
1385        let clock_account = account::create_account_shared_data_for_test(&clock);
1386        let transaction_accounts = vec![
1387            (vote_pubkey, vote_account),
1388            (sysvar::clock::id(), clock_account),
1389            (current_authority_base_key, AccountSharedData::default()),
1390            (new_authority_pubkey, AccountSharedData::default()),
1391        ];
1392        let mut instruction_accounts = vec![
1393            AccountMeta {
1394                pubkey: vote_pubkey,
1395                is_signer: false,
1396                is_writable: true,
1397            },
1398            AccountMeta {
1399                pubkey: sysvar::clock::id(),
1400                is_signer: false,
1401                is_writable: false,
1402            },
1403            AccountMeta {
1404                pubkey: current_authority_base_key,
1405                is_signer: true,
1406                is_writable: false,
1407            },
1408            AccountMeta {
1409                pubkey: new_authority_pubkey,
1410                is_signer: true,
1411                is_writable: false,
1412            },
1413        ];
1414
1415        // Can't change authority unless base key signs.
1416        instruction_accounts[2].is_signer = false;
1417        process_instruction(
1418            &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
1419                VoteAuthorizeCheckedWithSeedArgs {
1420                    authorization_type,
1421                    current_authority_derived_key_owner: current_authority_owner,
1422                    current_authority_derived_key_seed: current_authority_seed.clone(),
1423                },
1424            ))
1425            .unwrap(),
1426            transaction_accounts.clone(),
1427            instruction_accounts.clone(),
1428            Err(InstructionError::MissingRequiredSignature),
1429        );
1430        instruction_accounts[2].is_signer = true;
1431
1432        // Can't change authority unless new authority signs.
1433        instruction_accounts[3].is_signer = false;
1434        process_instruction(
1435            &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
1436                VoteAuthorizeCheckedWithSeedArgs {
1437                    authorization_type,
1438                    current_authority_derived_key_owner: current_authority_owner,
1439                    current_authority_derived_key_seed: current_authority_seed.clone(),
1440                },
1441            ))
1442            .unwrap(),
1443            transaction_accounts.clone(),
1444            instruction_accounts.clone(),
1445            Err(InstructionError::MissingRequiredSignature),
1446        );
1447        instruction_accounts[3].is_signer = true;
1448
1449        // Can't change authority if seed doesn't match.
1450        process_instruction(
1451            &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
1452                VoteAuthorizeCheckedWithSeedArgs {
1453                    authorization_type,
1454                    current_authority_derived_key_owner: current_authority_owner,
1455                    current_authority_derived_key_seed: String::from("WRONG_SEED"),
1456                },
1457            ))
1458            .unwrap(),
1459            transaction_accounts.clone(),
1460            instruction_accounts.clone(),
1461            Err(InstructionError::MissingRequiredSignature),
1462        );
1463
1464        // Can't change authority if owner doesn't match.
1465        process_instruction(
1466            &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
1467                VoteAuthorizeCheckedWithSeedArgs {
1468                    authorization_type,
1469                    current_authority_derived_key_owner: Pubkey::new_unique(), // Wrong owner.
1470                    current_authority_derived_key_seed: current_authority_seed.clone(),
1471                },
1472            ))
1473            .unwrap(),
1474            transaction_accounts.clone(),
1475            instruction_accounts.clone(),
1476            Err(InstructionError::MissingRequiredSignature),
1477        );
1478
1479        // Can change authority when base key signs for related derived key and new authority signs.
1480        process_instruction(
1481            &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
1482                VoteAuthorizeCheckedWithSeedArgs {
1483                    authorization_type,
1484                    current_authority_derived_key_owner: current_authority_owner,
1485                    current_authority_derived_key_seed: current_authority_seed.clone(),
1486                },
1487            ))
1488            .unwrap(),
1489            transaction_accounts.clone(),
1490            instruction_accounts.clone(),
1491            Ok(()),
1492        );
1493
1494        // Should fail when the `vote_authorize_with_seed` feature is disabled
1495        process_instruction_disabled_features(
1496            &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
1497                VoteAuthorizeCheckedWithSeedArgs {
1498                    authorization_type,
1499                    current_authority_derived_key_owner: current_authority_owner,
1500                    current_authority_derived_key_seed: current_authority_seed,
1501                },
1502            ))
1503            .unwrap(),
1504            transaction_accounts,
1505            instruction_accounts,
1506            Err(InstructionError::InvalidInstructionData),
1507        );
1508    }
1509
1510    #[test]
1511    fn test_voter_base_key_can_authorize_new_voter() {
1512        let VoteAccountTestFixtureWithAuthorities {
1513            vote_pubkey,
1514            voter_base_key,
1515            voter_owner,
1516            voter_seed,
1517            vote_account,
1518            ..
1519        } = create_test_account_with_authorized_from_seed();
1520        let new_voter_pubkey = Pubkey::new_unique();
1521        perform_authorize_with_seed_test(
1522            VoteAuthorize::Voter,
1523            vote_pubkey,
1524            vote_account,
1525            voter_base_key,
1526            voter_seed,
1527            voter_owner,
1528            new_voter_pubkey,
1529        );
1530    }
1531
1532    #[test]
1533    fn test_withdrawer_base_key_can_authorize_new_voter() {
1534        let VoteAccountTestFixtureWithAuthorities {
1535            vote_pubkey,
1536            withdrawer_base_key,
1537            withdrawer_owner,
1538            withdrawer_seed,
1539            vote_account,
1540            ..
1541        } = create_test_account_with_authorized_from_seed();
1542        let new_voter_pubkey = Pubkey::new_unique();
1543        perform_authorize_with_seed_test(
1544            VoteAuthorize::Voter,
1545            vote_pubkey,
1546            vote_account,
1547            withdrawer_base_key,
1548            withdrawer_seed,
1549            withdrawer_owner,
1550            new_voter_pubkey,
1551        );
1552    }
1553
1554    #[test]
1555    fn test_voter_base_key_can_not_authorize_new_withdrawer() {
1556        let VoteAccountTestFixtureWithAuthorities {
1557            vote_pubkey,
1558            voter_base_key,
1559            voter_owner,
1560            voter_seed,
1561            vote_account,
1562            ..
1563        } = create_test_account_with_authorized_from_seed();
1564        let new_withdrawer_pubkey = Pubkey::new_unique();
1565        let clock = Clock {
1566            epoch: 1,
1567            leader_schedule_epoch: 2,
1568            ..Clock::default()
1569        };
1570        let clock_account = account::create_account_shared_data_for_test(&clock);
1571        let transaction_accounts = vec![
1572            (vote_pubkey, vote_account),
1573            (sysvar::clock::id(), clock_account),
1574            (voter_base_key, AccountSharedData::default()),
1575        ];
1576        let instruction_accounts = vec![
1577            AccountMeta {
1578                pubkey: vote_pubkey,
1579                is_signer: false,
1580                is_writable: true,
1581            },
1582            AccountMeta {
1583                pubkey: sysvar::clock::id(),
1584                is_signer: false,
1585                is_writable: false,
1586            },
1587            AccountMeta {
1588                pubkey: voter_base_key,
1589                is_signer: true,
1590                is_writable: false,
1591            },
1592        ];
1593        // Despite having Voter authority, you may not change the Withdrawer authority.
1594        process_instruction(
1595            &serialize(&VoteInstruction::AuthorizeWithSeed(
1596                VoteAuthorizeWithSeedArgs {
1597                    authorization_type: VoteAuthorize::Withdrawer,
1598                    current_authority_derived_key_owner: voter_owner,
1599                    current_authority_derived_key_seed: voter_seed,
1600                    new_authority: new_withdrawer_pubkey,
1601                },
1602            ))
1603            .unwrap(),
1604            transaction_accounts,
1605            instruction_accounts,
1606            Err(InstructionError::MissingRequiredSignature),
1607        );
1608    }
1609
1610    #[test]
1611    fn test_withdrawer_base_key_can_authorize_new_withdrawer() {
1612        let VoteAccountTestFixtureWithAuthorities {
1613            vote_pubkey,
1614            withdrawer_base_key,
1615            withdrawer_owner,
1616            withdrawer_seed,
1617            vote_account,
1618            ..
1619        } = create_test_account_with_authorized_from_seed();
1620        let new_withdrawer_pubkey = Pubkey::new_unique();
1621        perform_authorize_with_seed_test(
1622            VoteAuthorize::Withdrawer,
1623            vote_pubkey,
1624            vote_account,
1625            withdrawer_base_key,
1626            withdrawer_seed,
1627            withdrawer_owner,
1628            new_withdrawer_pubkey,
1629        );
1630    }
1631
1632    #[test]
1633    fn test_voter_base_key_can_authorize_new_voter_checked() {
1634        let VoteAccountTestFixtureWithAuthorities {
1635            vote_pubkey,
1636            voter_base_key,
1637            voter_owner,
1638            voter_seed,
1639            vote_account,
1640            ..
1641        } = create_test_account_with_authorized_from_seed();
1642        let new_voter_pubkey = Pubkey::new_unique();
1643        perform_authorize_checked_with_seed_test(
1644            VoteAuthorize::Voter,
1645            vote_pubkey,
1646            vote_account,
1647            voter_base_key,
1648            voter_seed,
1649            voter_owner,
1650            new_voter_pubkey,
1651        );
1652    }
1653
1654    #[test]
1655    fn test_withdrawer_base_key_can_authorize_new_voter_checked() {
1656        let VoteAccountTestFixtureWithAuthorities {
1657            vote_pubkey,
1658            withdrawer_base_key,
1659            withdrawer_owner,
1660            withdrawer_seed,
1661            vote_account,
1662            ..
1663        } = create_test_account_with_authorized_from_seed();
1664        let new_voter_pubkey = Pubkey::new_unique();
1665        perform_authorize_checked_with_seed_test(
1666            VoteAuthorize::Voter,
1667            vote_pubkey,
1668            vote_account,
1669            withdrawer_base_key,
1670            withdrawer_seed,
1671            withdrawer_owner,
1672            new_voter_pubkey,
1673        );
1674    }
1675
1676    #[test]
1677    fn test_voter_base_key_can_not_authorize_new_withdrawer_checked() {
1678        let VoteAccountTestFixtureWithAuthorities {
1679            vote_pubkey,
1680            voter_base_key,
1681            voter_owner,
1682            voter_seed,
1683            vote_account,
1684            ..
1685        } = create_test_account_with_authorized_from_seed();
1686        let new_withdrawer_pubkey = Pubkey::new_unique();
1687        let clock = Clock {
1688            epoch: 1,
1689            leader_schedule_epoch: 2,
1690            ..Clock::default()
1691        };
1692        let clock_account = account::create_account_shared_data_for_test(&clock);
1693        let transaction_accounts = vec![
1694            (vote_pubkey, vote_account),
1695            (sysvar::clock::id(), clock_account),
1696            (voter_base_key, AccountSharedData::default()),
1697            (new_withdrawer_pubkey, AccountSharedData::default()),
1698        ];
1699        let instruction_accounts = vec![
1700            AccountMeta {
1701                pubkey: vote_pubkey,
1702                is_signer: false,
1703                is_writable: true,
1704            },
1705            AccountMeta {
1706                pubkey: sysvar::clock::id(),
1707                is_signer: false,
1708                is_writable: false,
1709            },
1710            AccountMeta {
1711                pubkey: voter_base_key,
1712                is_signer: true,
1713                is_writable: false,
1714            },
1715            AccountMeta {
1716                pubkey: new_withdrawer_pubkey,
1717                is_signer: true,
1718                is_writable: false,
1719            },
1720        ];
1721        // Despite having Voter authority, you may not change the Withdrawer authority.
1722        process_instruction(
1723            &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
1724                VoteAuthorizeCheckedWithSeedArgs {
1725                    authorization_type: VoteAuthorize::Withdrawer,
1726                    current_authority_derived_key_owner: voter_owner,
1727                    current_authority_derived_key_seed: voter_seed,
1728                },
1729            ))
1730            .unwrap(),
1731            transaction_accounts,
1732            instruction_accounts,
1733            Err(InstructionError::MissingRequiredSignature),
1734        );
1735    }
1736
1737    #[test]
1738    fn test_withdrawer_base_key_can_authorize_new_withdrawer_checked() {
1739        let VoteAccountTestFixtureWithAuthorities {
1740            vote_pubkey,
1741            withdrawer_base_key,
1742            withdrawer_owner,
1743            withdrawer_seed,
1744            vote_account,
1745            ..
1746        } = create_test_account_with_authorized_from_seed();
1747        let new_withdrawer_pubkey = Pubkey::new_unique();
1748        perform_authorize_checked_with_seed_test(
1749            VoteAuthorize::Withdrawer,
1750            vote_pubkey,
1751            vote_account,
1752            withdrawer_base_key,
1753            withdrawer_seed,
1754            withdrawer_owner,
1755            new_withdrawer_pubkey,
1756        );
1757    }
1758
1759    #[test]
1760    fn test_spoofed_vote() {
1761        process_instruction_as_one_arg(
1762            &vote(
1763                &invalid_vote_state_pubkey(),
1764                &Pubkey::new_unique(),
1765                Vote::default(),
1766            ),
1767            Err(InstructionError::InvalidAccountOwner),
1768        );
1769        process_instruction_as_one_arg(
1770            &update_vote_state(
1771                &invalid_vote_state_pubkey(),
1772                &Pubkey::default(),
1773                VoteStateUpdate::default(),
1774            ),
1775            Err(InstructionError::InvalidAccountOwner),
1776        );
1777    }
1778
1779    #[test]
1780    fn test_vote_process_instruction() {
1781        solana_logger::setup();
1782        let instructions = create_account(
1783            &Pubkey::new_unique(),
1784            &Pubkey::new_unique(),
1785            &VoteInit::default(),
1786            101,
1787        );
1788        process_instruction_as_one_arg(&instructions[1], Err(InstructionError::InvalidAccountData));
1789        process_instruction_as_one_arg(
1790            &vote(
1791                &Pubkey::new_unique(),
1792                &Pubkey::new_unique(),
1793                Vote::default(),
1794            ),
1795            Err(InstructionError::InvalidAccountData),
1796        );
1797        process_instruction_as_one_arg(
1798            &vote_switch(
1799                &Pubkey::new_unique(),
1800                &Pubkey::new_unique(),
1801                Vote::default(),
1802                Hash::default(),
1803            ),
1804            Err(InstructionError::InvalidAccountData),
1805        );
1806        process_instruction_as_one_arg(
1807            &authorize(
1808                &Pubkey::new_unique(),
1809                &Pubkey::new_unique(),
1810                &Pubkey::new_unique(),
1811                VoteAuthorize::Voter,
1812            ),
1813            Err(InstructionError::InvalidAccountData),
1814        );
1815        process_instruction_as_one_arg(
1816            &update_vote_state(
1817                &Pubkey::default(),
1818                &Pubkey::default(),
1819                VoteStateUpdate::default(),
1820            ),
1821            Err(InstructionError::InvalidAccountData),
1822        );
1823
1824        process_instruction_as_one_arg(
1825            &update_vote_state_switch(
1826                &Pubkey::default(),
1827                &Pubkey::default(),
1828                VoteStateUpdate::default(),
1829                Hash::default(),
1830            ),
1831            Err(InstructionError::InvalidAccountData),
1832        );
1833
1834        process_instruction_as_one_arg(
1835            &update_validator_identity(
1836                &Pubkey::new_unique(),
1837                &Pubkey::new_unique(),
1838                &Pubkey::new_unique(),
1839            ),
1840            Err(InstructionError::InvalidAccountData),
1841        );
1842        process_instruction_as_one_arg(
1843            &update_commission(&Pubkey::new_unique(), &Pubkey::new_unique(), 0),
1844            Err(InstructionError::InvalidAccountData),
1845        );
1846
1847        process_instruction_as_one_arg(
1848            &withdraw(
1849                &Pubkey::new_unique(),
1850                &Pubkey::new_unique(),
1851                0,
1852                &Pubkey::new_unique(),
1853            ),
1854            Err(InstructionError::InvalidAccountData),
1855        );
1856    }
1857
1858    #[test]
1859    fn test_vote_authorize_checked() {
1860        let vote_pubkey = Pubkey::new_unique();
1861        let authorized_pubkey = Pubkey::new_unique();
1862        let new_authorized_pubkey = Pubkey::new_unique();
1863
1864        // Test with vanilla authorize accounts
1865        let mut instruction = authorize_checked(
1866            &vote_pubkey,
1867            &authorized_pubkey,
1868            &new_authorized_pubkey,
1869            VoteAuthorize::Voter,
1870        );
1871        instruction.accounts = instruction.accounts[0..2].to_vec();
1872        process_instruction_as_one_arg(&instruction, Err(InstructionError::NotEnoughAccountKeys));
1873
1874        let mut instruction = authorize_checked(
1875            &vote_pubkey,
1876            &authorized_pubkey,
1877            &new_authorized_pubkey,
1878            VoteAuthorize::Withdrawer,
1879        );
1880        instruction.accounts = instruction.accounts[0..2].to_vec();
1881        process_instruction_as_one_arg(&instruction, Err(InstructionError::NotEnoughAccountKeys));
1882
1883        // Test with non-signing new_authorized_pubkey
1884        let mut instruction = authorize_checked(
1885            &vote_pubkey,
1886            &authorized_pubkey,
1887            &new_authorized_pubkey,
1888            VoteAuthorize::Voter,
1889        );
1890        instruction.accounts[3] = AccountMeta::new_readonly(new_authorized_pubkey, false);
1891        process_instruction_as_one_arg(
1892            &instruction,
1893            Err(InstructionError::MissingRequiredSignature),
1894        );
1895
1896        let mut instruction = authorize_checked(
1897            &vote_pubkey,
1898            &authorized_pubkey,
1899            &new_authorized_pubkey,
1900            VoteAuthorize::Withdrawer,
1901        );
1902        instruction.accounts[3] = AccountMeta::new_readonly(new_authorized_pubkey, false);
1903        process_instruction_as_one_arg(
1904            &instruction,
1905            Err(InstructionError::MissingRequiredSignature),
1906        );
1907
1908        // Test with new_authorized_pubkey signer
1909        let vote_account = AccountSharedData::new(100, VoteState::size_of(), &id());
1910        let clock_address = sysvar::clock::id();
1911        let clock_account = account::create_account_shared_data_for_test(&Clock::default());
1912        let default_authorized_pubkey = Pubkey::default();
1913        let authorized_account = create_default_account();
1914        let new_authorized_account = create_default_account();
1915        let transaction_accounts = vec![
1916            (vote_pubkey, vote_account),
1917            (clock_address, clock_account),
1918            (default_authorized_pubkey, authorized_account),
1919            (new_authorized_pubkey, new_authorized_account),
1920        ];
1921        let instruction_accounts = vec![
1922            AccountMeta {
1923                pubkey: vote_pubkey,
1924                is_signer: false,
1925                is_writable: true,
1926            },
1927            AccountMeta {
1928                pubkey: clock_address,
1929                is_signer: false,
1930                is_writable: false,
1931            },
1932            AccountMeta {
1933                pubkey: default_authorized_pubkey,
1934                is_signer: true,
1935                is_writable: false,
1936            },
1937            AccountMeta {
1938                pubkey: new_authorized_pubkey,
1939                is_signer: true,
1940                is_writable: false,
1941            },
1942        ];
1943        process_instruction(
1944            &serialize(&VoteInstruction::AuthorizeChecked(VoteAuthorize::Voter)).unwrap(),
1945            transaction_accounts.clone(),
1946            instruction_accounts.clone(),
1947            Ok(()),
1948        );
1949        process_instruction(
1950            &serialize(&VoteInstruction::AuthorizeChecked(
1951                VoteAuthorize::Withdrawer,
1952            ))
1953            .unwrap(),
1954            transaction_accounts,
1955            instruction_accounts,
1956            Ok(()),
1957        );
1958    }
1959}