1use {
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 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 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 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 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 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 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 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 (
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 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 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 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 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 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 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 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 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 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 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 let accounts = process_instruction(
903 &instruction_data,
904 transaction_accounts.clone(),
905 instruction_accounts.clone(),
906 Ok(()),
907 );
908
909 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 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 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 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 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 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 let accounts = process_instruction(
1017 &instruction_data,
1018 transaction_accounts.clone(),
1019 instruction_accounts.clone(),
1020 Ok(()),
1021 );
1022
1023 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 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 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 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 assert!(post_state.is_uninitialized());
1115
1116 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 process_instruction(
1128 &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
1129 transaction_accounts.clone(),
1130 instruction_accounts.clone(),
1131 Ok(()),
1132 );
1133
1134 process_instruction(
1136 &serialize(&VoteInstruction::Withdraw(lamports + 1)).unwrap(),
1137 transaction_accounts.clone(),
1138 instruction_accounts.clone(),
1139 Err(InstructionError::InsufficientFunds),
1140 );
1141
1142 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 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 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 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 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 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 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 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 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 process_instruction(
1326 &serialize(&VoteInstruction::AuthorizeWithSeed(
1327 VoteAuthorizeWithSeedArgs {
1328 authorization_type,
1329 current_authority_derived_key_owner: Pubkey::new_unique(), 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 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 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 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 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 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 process_instruction(
1468 &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
1469 VoteAuthorizeCheckedWithSeedArgs {
1470 authorization_type,
1471 current_authority_derived_key_owner: Pubkey::new_unique(), 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 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 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 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 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 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 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 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}