solana_vote_program/
vote_processor.rs

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