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