1use {
4 crate::vote_state::{self, handler::VoteStateTargetVersion},
5 log::*,
6 solana_bincode::limited_deserialize,
7 solana_instruction::error::InstructionError,
8 solana_program_runtime::{
9 declare_process_instruction, invoke_context::InvokeContext,
10 sysvar_cache::get_sysvar_with_account_check,
11 },
12 solana_pubkey::Pubkey,
13 solana_transaction_context::{
14 instruction::InstructionContext, instruction_accounts::BorrowedInstructionAccount,
15 },
16 solana_vote_interface::{instruction::VoteInstruction, program::id, state::VoteAuthorize},
17 std::collections::HashSet,
18};
19
20#[allow(clippy::too_many_arguments)]
21fn process_authorize_with_seed_instruction<F>(
22 invoke_context: &InvokeContext,
23 instruction_context: &InstructionContext,
24 vote_account: &mut BorrowedInstructionAccount,
25 target_version: VoteStateTargetVersion,
26 new_authority: &Pubkey,
27 authorization_type: VoteAuthorize,
28 current_authority_derived_key_owner: &Pubkey,
29 current_authority_derived_key_seed: &str,
30 is_vote_authorize_with_bls_enabled: bool,
31 consume_pop_compute_units: F,
32) -> Result<(), InstructionError>
33where
34 F: FnOnce() -> Result<(), InstructionError>,
35{
36 let clock = get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?;
37 let mut expected_authority_keys: HashSet<Pubkey> = HashSet::default();
38 if instruction_context.is_instruction_account_signer(2)? {
39 let base_pubkey = instruction_context.get_key_of_instruction_account(2)?;
40 expected_authority_keys.insert(
43 Pubkey::create_with_seed(
44 base_pubkey,
45 current_authority_derived_key_seed,
46 current_authority_derived_key_owner,
47 )
48 .map_err(|e| e as u64)?,
49 );
50 };
51 vote_state::authorize(
52 vote_account,
53 target_version,
54 new_authority,
55 authorization_type,
56 &expected_authority_keys,
57 &clock,
58 is_vote_authorize_with_bls_enabled,
59 consume_pop_compute_units,
60 )
61}
62
63fn is_init_account_v2_enabled(invoke_context: &InvokeContext) -> bool {
64 let feature_set = invoke_context.get_feature_set();
65 feature_set.bls_pubkey_management_in_vote_account
66 && feature_set.commission_rate_in_basis_points
67 && feature_set.custom_commission_collector
68 && feature_set.block_revenue_sharing
69 && feature_set.vote_account_initialize_v2
70}
71
72fn is_vote_authorize_with_bls_enabled(invoke_context: &InvokeContext) -> bool {
73 invoke_context
74 .get_feature_set()
75 .bls_pubkey_management_in_vote_account
76}
77
78fn should_reject_legacy_vote_instructions(invoke_context: &InvokeContext) -> bool {
79 invoke_context.is_deprecate_legacy_vote_ixs_active()
80 || invoke_context.is_alpenglow_migration_succeeded()
81}
82
83pub const DEFAULT_COMPUTE_UNITS: u64 = 2_100;
86
87pub const BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS: u64 = 34_500;
89
90declare_process_instruction!(Entrypoint, DEFAULT_COMPUTE_UNITS, |invoke_context| {
91 let transaction_context = &invoke_context.transaction_context;
92 let instruction_context = transaction_context.get_current_instruction_context()?;
93 let data = instruction_context.get_instruction_data();
94
95 trace!("process_instruction: {data:?}");
96
97 let mut me = instruction_context.try_borrow_instruction_account(0)?;
98 if *me.get_owner() != id() {
99 return Err(InstructionError::InvalidAccountOwner);
100 }
101
102 let target_version = VoteStateTargetVersion::V4;
104
105 let signers = instruction_context.get_signers()?;
106 let is_init_account_v2_enabled = is_init_account_v2_enabled(invoke_context);
107 let is_vote_authorize_with_bls_enabled = is_vote_authorize_with_bls_enabled(invoke_context);
108 let consume_pop_compute_units = || {
109 invoke_context
110 .compute_meter
111 .consume_checked(BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS)
112 .map_err(|_| InstructionError::ComputationalBudgetExceeded)
113 };
114 match limited_deserialize(data, solana_packet::PACKET_DATA_SIZE as u64)? {
115 VoteInstruction::InitializeAccount(vote_init) => {
116 let rent =
117 get_sysvar_with_account_check::rent(invoke_context, &instruction_context, 1)?;
118 if !rent.is_exempt(me.get_lamports(), me.get_data().len()) {
119 return Err(InstructionError::InsufficientFunds);
120 }
121 let clock =
122 get_sysvar_with_account_check::clock(invoke_context, &instruction_context, 2)?;
123 vote_state::initialize_account(&mut me, target_version, &vote_init, &signers, &clock)
124 }
125 VoteInstruction::Authorize(voter_pubkey, vote_authorize) => {
126 let clock =
127 get_sysvar_with_account_check::clock(invoke_context, &instruction_context, 1)?;
128 vote_state::authorize(
129 &mut me,
130 target_version,
131 &voter_pubkey,
132 vote_authorize,
133 &signers,
134 &clock,
135 is_vote_authorize_with_bls_enabled,
136 consume_pop_compute_units,
137 )
138 }
139 VoteInstruction::AuthorizeWithSeed(args) => {
140 instruction_context.check_number_of_instruction_accounts(3)?;
141 process_authorize_with_seed_instruction(
142 invoke_context,
143 &instruction_context,
144 &mut me,
145 target_version,
146 &args.new_authority,
147 args.authorization_type,
148 &args.current_authority_derived_key_owner,
149 args.current_authority_derived_key_seed.as_str(),
150 is_vote_authorize_with_bls_enabled,
151 consume_pop_compute_units,
152 )
153 }
154 VoteInstruction::AuthorizeCheckedWithSeed(args) => {
155 instruction_context.check_number_of_instruction_accounts(4)?;
156 let new_authority = instruction_context.get_key_of_instruction_account(3)?;
157 if !instruction_context.is_instruction_account_signer(3)? {
158 return Err(InstructionError::MissingRequiredSignature);
159 }
160 process_authorize_with_seed_instruction(
161 invoke_context,
162 &instruction_context,
163 &mut me,
164 target_version,
165 new_authority,
166 args.authorization_type,
167 &args.current_authority_derived_key_owner,
168 args.current_authority_derived_key_seed.as_str(),
169 is_vote_authorize_with_bls_enabled,
170 consume_pop_compute_units,
171 )
172 }
173 VoteInstruction::UpdateValidatorIdentity => {
174 instruction_context.check_number_of_instruction_accounts(2)?;
175 let node_pubkey = instruction_context.get_key_of_instruction_account(1)?;
176 let custom_collector_enabled =
177 invoke_context.get_feature_set().custom_commission_collector;
178 vote_state::update_validator_identity(
179 &mut me,
180 target_version,
181 node_pubkey,
182 &signers,
183 custom_collector_enabled,
184 )
185 }
186 VoteInstruction::UpdateCommission(commission) => {
187 let sysvar_cache = invoke_context.environment_config.sysvar_cache();
188
189 let disable_commission_update_rule =
193 invoke_context.get_feature_set().delay_commission_updates;
194
195 vote_state::update_commission(
196 &mut me,
197 target_version,
198 commission,
199 &signers,
200 sysvar_cache.get_epoch_schedule()?.as_ref(),
201 sysvar_cache.get_clock()?.as_ref(),
202 disable_commission_update_rule,
203 )
204 }
205 VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => {
206 if should_reject_legacy_vote_instructions(invoke_context) {
207 return Err(InstructionError::InvalidInstructionData);
208 }
209 let slot_hashes = get_sysvar_with_account_check::slot_hashes(
210 invoke_context,
211 &instruction_context,
212 1,
213 )?;
214 let clock =
215 get_sysvar_with_account_check::clock(invoke_context, &instruction_context, 2)?;
216 vote_state::process_vote_with_account(
217 &mut me,
218 target_version,
219 &slot_hashes,
220 &clock,
221 &vote,
222 &signers,
223 )
224 }
225 VoteInstruction::UpdateVoteState(vote_state_update)
226 | VoteInstruction::UpdateVoteStateSwitch(vote_state_update, _) => {
227 if should_reject_legacy_vote_instructions(invoke_context) {
228 return Err(InstructionError::InvalidInstructionData);
229 }
230 let sysvar_cache = invoke_context.environment_config.sysvar_cache();
231 let slot_hashes = sysvar_cache.get_slot_hashes()?;
232 let clock = sysvar_cache.get_clock()?;
233 vote_state::process_vote_state_update(
234 &mut me,
235 target_version,
236 slot_hashes.slot_hashes(),
237 &clock,
238 vote_state_update,
239 &signers,
240 )
241 }
242 VoteInstruction::CompactUpdateVoteState(vote_state_update)
243 | VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
244 if should_reject_legacy_vote_instructions(invoke_context) {
245 return Err(InstructionError::InvalidInstructionData);
246 }
247 let sysvar_cache = invoke_context.environment_config.sysvar_cache();
248 let slot_hashes = sysvar_cache.get_slot_hashes()?;
249 let clock = sysvar_cache.get_clock()?;
250 vote_state::process_vote_state_update(
251 &mut me,
252 target_version,
253 slot_hashes.slot_hashes(),
254 &clock,
255 vote_state_update,
256 &signers,
257 )
258 }
259 VoteInstruction::TowerSync(tower_sync)
260 | VoteInstruction::TowerSyncSwitch(tower_sync, _) => {
261 if invoke_context.is_alpenglow_migration_succeeded() {
262 return Err(InstructionError::InvalidInstructionData);
263 }
264 let sysvar_cache = invoke_context.environment_config.sysvar_cache();
265 let slot_hashes = sysvar_cache.get_slot_hashes()?;
266 let clock = sysvar_cache.get_clock()?;
267 vote_state::process_tower_sync(
268 &mut me,
269 target_version,
270 slot_hashes.slot_hashes(),
271 &clock,
272 tower_sync,
273 &signers,
274 )
275 }
276 VoteInstruction::Withdraw(lamports) => {
277 instruction_context.check_number_of_instruction_accounts(2)?;
278 let rent_sysvar = invoke_context
279 .environment_config
280 .sysvar_cache()
281 .get_rent()?;
282 let clock_sysvar = invoke_context
283 .environment_config
284 .sysvar_cache()
285 .get_clock()?;
286
287 drop(me);
288 vote_state::withdraw(
289 &instruction_context,
290 0,
291 target_version,
292 lamports,
293 1,
294 &signers,
295 &rent_sysvar,
296 &clock_sysvar,
297 )
298 }
299 VoteInstruction::AuthorizeChecked(vote_authorize) => {
300 instruction_context.check_number_of_instruction_accounts(4)?;
301 let voter_pubkey = instruction_context.get_key_of_instruction_account(3)?;
302 if !instruction_context.is_instruction_account_signer(3)? {
303 return Err(InstructionError::MissingRequiredSignature);
304 }
305 let clock =
306 get_sysvar_with_account_check::clock(invoke_context, &instruction_context, 1)?;
307 vote_state::authorize(
308 &mut me,
309 target_version,
310 voter_pubkey,
311 vote_authorize,
312 &signers,
313 &clock,
314 is_vote_authorize_with_bls_enabled,
315 consume_pop_compute_units,
316 )
317 }
318 VoteInstruction::InitializeAccountV2(vote_init_v2) => {
319 if !is_init_account_v2_enabled {
320 return Err(InstructionError::InvalidInstructionData);
321 }
322
323 instruction_context.check_number_of_instruction_accounts(4)?;
324
325 let sysvar_cache = invoke_context.environment_config.sysvar_cache();
326 let clock = sysvar_cache.get_clock()?;
327 let rent = sysvar_cache.get_rent()?;
328
329 drop(me);
330 vote_state::initialize_account_v2(
331 &instruction_context,
332 0,
333 target_version,
334 &vote_init_v2,
335 2,
336 3,
337 &signers,
338 &clock,
339 &rent,
340 consume_pop_compute_units,
341 )
342 }
343 VoteInstruction::UpdateCommissionBps {
344 commission_bps,
345 kind,
346 } => {
347 let feature_set = invoke_context.get_feature_set();
351 if !feature_set.commission_rate_in_basis_points || !feature_set.delay_commission_updates
352 {
353 return Err(InstructionError::InvalidInstructionData);
354 }
355 vote_state::update_commission_bps(
356 &mut me,
357 target_version,
358 commission_bps,
359 kind,
360 &signers,
361 feature_set.block_revenue_sharing,
362 )
363 }
364 VoteInstruction::UpdateCommissionCollector(kind) => {
365 let custom_collector_enabled =
368 invoke_context.get_feature_set().custom_commission_collector;
369 if !custom_collector_enabled {
370 return Err(InstructionError::InvalidInstructionData);
371 }
372
373 instruction_context.check_number_of_instruction_accounts(3)?;
374
375 let rent = invoke_context
376 .environment_config
377 .sysvar_cache()
378 .get_rent()?;
379
380 drop(me);
381 vote_state::update_commission_collector(
382 &instruction_context,
383 0,
384 target_version,
385 1,
386 kind,
387 &signers,
388 &rent,
389 )
390 }
391 VoteInstruction::DepositDelegatorRewards { deposit } => {
392 let feature_set = invoke_context.get_feature_set();
398 if !feature_set.commission_rate_in_basis_points
399 || !feature_set.custom_commission_collector
400 || !feature_set.block_revenue_sharing
401 {
402 return Err(InstructionError::InvalidInstructionData);
403 }
404
405 instruction_context.check_number_of_instruction_accounts(2)?;
406 drop(me);
407 vote_state::deposit_delegator_rewards(invoke_context, 0, 1, deposit, &signers)
408 }
409 }
410});
411
412#[allow(clippy::arithmetic_side_effects)]
413#[cfg(test)]
414mod tests {
415 use {
416 super::*,
417 crate::{
418 vote_error::VoteError,
419 vote_instruction::{
420 CreateVoteAccountConfig, VoteInstruction, authorize, authorize_checked,
421 compact_update_vote_state, compact_update_vote_state_switch,
422 create_account_with_config, update_commission, update_validator_identity,
423 update_vote_state, update_vote_state_switch, vote, vote_switch, withdraw,
424 },
425 vote_state::{
426 self, Lockout, TowerSync, Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs,
427 VoteAuthorizeWithSeedArgs, VoteInit, VoteInitV2, VoteStateUpdate, VoteStateV3,
428 VoteStateV4, VoteStateVersions, create_bls_pubkey_and_proof_of_possession,
429 handler::VoteStateHandler,
430 },
431 },
432 bincode::serialize,
433 solana_account::{
434 Account, AccountSharedData, ReadableAccount, WritableAccount,
435 state_traits::StateMutWincode as _,
436 },
437 solana_clock::Clock,
438 solana_epoch_schedule::EpochSchedule,
439 solana_hash::Hash,
440 solana_instruction::{AccountMeta, Instruction},
441 solana_program_runtime::{
442 invoke_context::mock_process_instruction_with_feature_set,
443 program_cache_entry::ProgramCacheEntry,
444 solana_sbpf::{program::BuiltinFunctionDefinition, vm::ContextObject},
445 },
446 solana_pubkey::Pubkey,
447 solana_rent::Rent,
448 solana_sdk_ids::sysvar,
449 solana_slot_hashes::SlotHashes,
450 solana_svm_feature_set::SVMFeatureSet,
451 solana_system_program::system_processor::DEFAULT_COMPUTE_UNITS as SYSTEM_PROGRAM_COMPUTE_UNITS,
452 solana_sysvar_id::SysvarId,
453 solana_vote_interface::{
454 instruction::{CommissionKind, tower_sync, tower_sync_switch},
455 state::{
456 BLS_PROOF_OF_POSSESSION_COMPRESSED_SIZE, BLS_PUBLIC_KEY_COMPRESSED_SIZE,
457 VoterWithBLSArgs,
458 },
459 },
460 std::{cell::RefCell, collections::HashSet, str::FromStr, sync::Arc},
461 test_case::test_matrix,
462 };
463
464 fn create_sysvar_account<T>(value: &T) -> AccountSharedData
465 where
466 T: wincode::Serialize<Src = T> + SysvarId,
467 {
468 let serialized_len = wincode::serialized_size(value).unwrap() as usize;
469 let canonical_data_len = match T::id() {
470 sysvar::clock::ID => solana_clock::SIZE,
471 sysvar::epoch_schedule::ID => solana_epoch_schedule::SIZE,
472 sysvar::rent::ID => solana_rent::SIZE,
473 sysvar::slot_hashes::ID => solana_slot_hashes::SIZE,
474 id => panic!("unsupported sysvar: {id}"),
475 };
476 let required_data_len = canonical_data_len.max(serialized_len);
477 let mut account = AccountSharedData::new(1, required_data_len, &sysvar::id());
478 wincode::serialize_into(account.data_as_mut_slice(), value).unwrap();
479 account
480 }
481
482 fn vote_state_size_of() -> usize {
483 VoteStateV4::size_of()
484 }
485
486 fn deserialize_vote_state_for_test(
487 account_data: &[u8],
488 vote_pubkey: &Pubkey,
489 ) -> VoteStateHandler {
490 VoteStateHandler::new_v4(VoteStateV4::deserialize(account_data, vote_pubkey).unwrap())
491 }
492
493 struct VoteAccountTestFixtureWithAuthorities {
494 vote_account: AccountSharedData,
495 vote_pubkey: Pubkey,
496 voter_base_key: Pubkey,
497 voter_owner: Pubkey,
498 voter_seed: String,
499 withdrawer_base_key: Pubkey,
500 withdrawer_owner: Pubkey,
501 withdrawer_seed: String,
502 }
503
504 fn create_default_account() -> AccountSharedData {
505 AccountSharedData::new(0, 0, &Pubkey::new_unique())
506 }
507
508 #[derive(Clone, Copy, Default)]
509 struct VoteProgramFeatures {
510 bls_pubkey_management_in_vote_account: bool,
511 commission_rate_in_basis_points: bool,
512 custom_commission_collector: bool,
513 block_revenue_sharing: bool,
514 vote_account_initialize_v2: bool,
515 alpenglow_migration_succeeded: bool,
516 }
517
518 impl VoteProgramFeatures {
519 fn all_enabled() -> Self {
520 Self {
521 bls_pubkey_management_in_vote_account: true,
522 commission_rate_in_basis_points: true,
523 custom_commission_collector: true,
524 block_revenue_sharing: true,
525 vote_account_initialize_v2: true,
526 alpenglow_migration_succeeded: false,
527 }
528 }
529 }
530
531 fn process_instruction(
532 features: VoteProgramFeatures,
533 instruction_data: &[u8],
534 transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
535 instruction_accounts: Vec<AccountMeta>,
536 expected_result: Result<(), InstructionError>,
537 ) -> Vec<AccountSharedData> {
538 process_instruction_with_cu_check(
539 features,
540 instruction_data,
541 transaction_accounts,
542 instruction_accounts,
543 expected_result,
544 DEFAULT_COMPUTE_UNITS,
545 )
546 }
547
548 fn process_instruction_with_cu_check(
549 features: VoteProgramFeatures,
550 instruction_data: &[u8],
551 transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
552 instruction_accounts: Vec<AccountMeta>,
553 expected_result: Result<(), InstructionError>,
554 expected_cus: u64,
555 ) -> Vec<AccountSharedData> {
556 let VoteProgramFeatures {
557 bls_pubkey_management_in_vote_account,
558 commission_rate_in_basis_points,
559 custom_commission_collector,
560 block_revenue_sharing,
561 vote_account_initialize_v2,
562 alpenglow_migration_succeeded,
563 } = features;
564 let cu_consumed = RefCell::new(0u64);
565 let accounts = mock_process_instruction_with_feature_set(
566 &id(),
567 instruction_data,
568 transaction_accounts,
569 instruction_accounts,
570 expected_result,
571 Entrypoint::register,
572 |invoke_context| {
573 invoke_context
574 .set_alpenglow_migration_succeeded_for_tests(alpenglow_migration_succeeded);
575 invoke_context.program_cache_for_tx_batch.replenish(
577 solana_sdk_ids::system_program::id(),
578 Arc::new(ProgramCacheEntry::new_builtin(
579 solana_system_program::system_processor::Entrypoint::register,
580 )),
581 );
582 *cu_consumed.borrow_mut() = invoke_context.get_remaining();
583 },
584 |invoke_context| {
585 *cu_consumed.borrow_mut() -= invoke_context.get_remaining();
586 },
587 &SVMFeatureSet {
588 bls_pubkey_management_in_vote_account,
589 commission_rate_in_basis_points,
590 custom_commission_collector,
591 block_revenue_sharing,
592 vote_account_initialize_v2,
593 ..SVMFeatureSet::all_enabled()
594 },
595 );
596 assert_eq!(
597 *cu_consumed.borrow(),
598 expected_cus,
599 "Expected {} CU consumed, got {}",
600 expected_cus,
601 *cu_consumed.borrow()
602 );
603 accounts
604 }
605
606 fn process_instruction_as_one_arg(
607 features: VoteProgramFeatures,
608 instruction: &Instruction,
609 expected_result: Result<(), InstructionError>,
610 ) -> Vec<AccountSharedData> {
611 process_instruction_as_one_arg_with_cu_check(
612 features,
613 instruction,
614 expected_result,
615 DEFAULT_COMPUTE_UNITS,
616 )
617 }
618
619 fn process_instruction_as_one_arg_with_cu_check(
620 features: VoteProgramFeatures,
621 instruction: &Instruction,
622 expected_result: Result<(), InstructionError>,
623 expected_cus: u64,
624 ) -> Vec<AccountSharedData> {
625 let mut pubkeys: HashSet<Pubkey> = instruction
626 .accounts
627 .iter()
628 .map(|meta| meta.pubkey)
629 .collect();
630 pubkeys.insert(sysvar::clock::id());
631 pubkeys.insert(sysvar::epoch_schedule::id());
632 pubkeys.insert(sysvar::rent::id());
633 pubkeys.insert(sysvar::slot_hashes::id());
634 let transaction_accounts: Vec<_> = pubkeys
635 .iter()
636 .map(|pubkey| {
637 (
638 *pubkey,
639 if sysvar::clock::check_id(pubkey) {
640 create_sysvar_account(&Clock::default())
641 } else if sysvar::epoch_schedule::check_id(pubkey) {
642 create_sysvar_account(&EpochSchedule::without_warmup())
643 } else if sysvar::slot_hashes::check_id(pubkey) {
644 create_sysvar_account(&SlotHashes::default())
645 } else if sysvar::rent::check_id(pubkey) {
646 create_sysvar_account(&Rent::free())
647 } else if *pubkey == invalid_vote_state_pubkey() {
648 AccountSharedData::from(Account {
649 owner: invalid_vote_state_pubkey(),
650 ..Account::default()
651 })
652 } else {
653 AccountSharedData::from(Account {
654 owner: id(),
655 ..Account::default()
656 })
657 },
658 )
659 })
660 .collect();
661 process_instruction_with_cu_check(
662 features,
663 &instruction.data,
664 transaction_accounts,
665 instruction.accounts.clone(),
666 expected_result,
667 expected_cus,
668 )
669 }
670
671 fn invalid_vote_state_pubkey() -> Pubkey {
672 Pubkey::from_str("BadVote111111111111111111111111111111111111").unwrap()
673 }
674
675 fn create_default_rent_account() -> AccountSharedData {
676 create_sysvar_account(&Rent::free())
677 }
678
679 fn create_default_clock_account() -> AccountSharedData {
680 create_sysvar_account(&Clock::default())
681 }
682
683 fn create_test_account() -> (Pubkey, AccountSharedData) {
684 let rent = Rent::default();
685 let vote_pubkey = solana_pubkey::new_rand();
686 let node_pubkey = solana_pubkey::new_rand();
687
688 let balance = rent.minimum_balance(VoteStateV4::size_of());
689 let account = vote_state::create_v4_account_with_authorized(
690 &node_pubkey,
691 &vote_pubkey,
692 [0u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE],
693 &vote_pubkey,
694 0,
695 &vote_pubkey,
696 0,
697 &node_pubkey,
698 balance,
699 );
700
701 (vote_pubkey, account)
702 }
703
704 fn create_test_account_no_bls_key() -> (Pubkey, AccountSharedData) {
708 let rent = Rent::default();
709 let vote_pubkey = solana_pubkey::new_rand();
710 let node_pubkey = solana_pubkey::new_rand();
711 let balance = rent.minimum_balance(VoteStateV4::size_of());
712
713 let mut account = AccountSharedData::new(balance, VoteStateV4::size_of(), &id());
714 let vote_state = VoteStateV4::new_with_defaults(
715 &vote_pubkey,
716 &VoteInit {
717 node_pubkey,
718 authorized_voter: vote_pubkey,
719 authorized_withdrawer: vote_pubkey,
720 commission: 0,
721 },
722 &Clock::default(),
723 );
724 VoteStateV4::serialize(
725 &VoteStateVersions::V4(Box::new(vote_state)),
726 account.data_as_mut_slice(),
727 )
728 .unwrap();
729
730 (vote_pubkey, account)
731 }
732
733 fn create_test_account_v3() -> (Pubkey, AccountSharedData) {
737 let rent = Rent::default();
738 let vote_pubkey = solana_pubkey::new_rand();
739 let node_pubkey = solana_pubkey::new_rand();
740 let balance = rent.minimum_balance(VoteStateV3::size_of());
741
742 let mut account = AccountSharedData::new(balance, VoteStateV3::size_of(), &id());
743 let vote_state = VoteStateV3::new(
744 &VoteInit {
745 node_pubkey,
746 authorized_voter: vote_pubkey,
747 authorized_withdrawer: vote_pubkey,
748 commission: 0,
749 },
750 &Clock::default(),
751 );
752 VoteStateV3::serialize(
753 &VoteStateVersions::V3(Box::new(vote_state)),
754 account.data_as_mut_slice(),
755 )
756 .unwrap();
757
758 (vote_pubkey, account)
759 }
760
761 fn create_test_account_with_authorized() -> (Pubkey, Pubkey, Pubkey, AccountSharedData) {
762 let vote_pubkey = solana_pubkey::new_rand();
763 let authorized_voter = solana_pubkey::new_rand();
764 let authorized_withdrawer = solana_pubkey::new_rand();
765 let account =
766 create_test_account_with_provided_authorized(&authorized_voter, &authorized_withdrawer);
767
768 (
769 vote_pubkey,
770 authorized_voter,
771 authorized_withdrawer,
772 account,
773 )
774 }
775
776 fn create_test_account_with_provided_authorized(
777 authorized_voter: &Pubkey,
778 authorized_withdrawer: &Pubkey,
779 ) -> AccountSharedData {
780 let node_pubkey = solana_pubkey::new_rand();
781
782 vote_state::create_v4_account_with_authorized(
783 &node_pubkey,
784 authorized_voter,
785 [0u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE],
786 authorized_withdrawer,
787 0,
788 authorized_withdrawer,
789 0,
790 &node_pubkey,
791 100,
792 )
793 }
794
795 fn create_test_account_with_authorized_from_seed() -> VoteAccountTestFixtureWithAuthorities {
796 let vote_pubkey = Pubkey::new_unique();
797 let voter_base_key = Pubkey::new_unique();
798 let voter_owner = Pubkey::new_unique();
799 let voter_seed = String::from("VOTER_SEED");
800 let withdrawer_base_key = Pubkey::new_unique();
801 let withdrawer_owner = Pubkey::new_unique();
802 let withdrawer_seed = String::from("WITHDRAWER_SEED");
803 let authorized_voter =
804 Pubkey::create_with_seed(&voter_base_key, voter_seed.as_str(), &voter_owner).unwrap();
805 let authorized_withdrawer = Pubkey::create_with_seed(
806 &withdrawer_base_key,
807 withdrawer_seed.as_str(),
808 &withdrawer_owner,
809 )
810 .unwrap();
811
812 let node_pubkey = Pubkey::new_unique();
813 let vote_account = vote_state::create_v4_account_with_authorized(
814 &node_pubkey,
815 &authorized_voter,
816 [0u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE],
817 &authorized_withdrawer,
818 0,
819 &authorized_withdrawer,
820 0,
821 &node_pubkey,
822 100,
823 );
824
825 VoteAccountTestFixtureWithAuthorities {
826 vote_account,
827 vote_pubkey,
828 voter_base_key,
829 voter_owner,
830 voter_seed,
831 withdrawer_base_key,
832 withdrawer_owner,
833 withdrawer_seed,
834 }
835 }
836
837 fn create_test_account_with_epoch_credits(
838 credits_to_append: &[u64],
839 ) -> (Pubkey, AccountSharedData) {
840 let vote_pubkey = solana_pubkey::new_rand();
841 let node_pubkey = solana_pubkey::new_rand();
842
843 let vote_init = VoteInit {
844 node_pubkey,
845 authorized_voter: vote_pubkey,
846 authorized_withdrawer: vote_pubkey,
847 commission: 0,
848 };
849 let clock = Clock::default();
850
851 let space = vote_state_size_of();
852 let lamports = Rent::default().minimum_balance(space);
853
854 let v4 = VoteStateV4::new_with_defaults(&vote_pubkey, &vote_init, &clock);
855 let mut vote_state = VoteStateHandler::new_v4(v4);
856
857 let epoch_credits = vote_state.epoch_credits_mut();
858 epoch_credits.clear();
859
860 let mut current_epoch_credits: u64 = 0;
861 let mut previous_epoch_credits = 0;
862 for (epoch, credits) in credits_to_append.iter().enumerate() {
863 current_epoch_credits = current_epoch_credits.saturating_add(*credits);
864 epoch_credits.push((
865 u64::try_from(epoch).unwrap(),
866 current_epoch_credits,
867 previous_epoch_credits,
868 ));
869 previous_epoch_credits = current_epoch_credits;
870 }
871
872 let mut account = AccountSharedData::new(lamports, space, &id());
873 account.set_data_from_slice(&vote_state.serialize());
874
875 (vote_pubkey, account)
876 }
877
878 fn create_serialized_votes() -> (Vote, Vec<(Vec<u8>, bool)>) {
881 let vote = Vote::new(vec![1], Hash::default());
882 let vote_state_update = VoteStateUpdate::from(vec![(1, 1)]);
883 let tower_sync = TowerSync::from(vec![(1, 1)]);
884 (
885 vote.clone(),
886 vec![
887 (serialize(&VoteInstruction::Vote(vote)).unwrap(), false),
888 (
889 serialize(&VoteInstruction::UpdateVoteState(vote_state_update.clone()))
890 .unwrap(),
891 false,
892 ),
893 (
894 serialize(&VoteInstruction::CompactUpdateVoteState(vote_state_update)).unwrap(),
895 false,
896 ),
897 (
898 serialize(&VoteInstruction::TowerSync(tower_sync)).unwrap(),
899 true,
900 ),
901 ],
902 )
903 }
904
905 #[test]
906 fn test_vote_process_instruction_decode_bail() {
907 process_instruction(
908 VoteProgramFeatures {
909 ..Default::default()
910 },
911 &[],
912 Vec::new(),
913 Vec::new(),
914 Err(InstructionError::MissingAccount),
915 );
916 }
917
918 #[test_matrix(
919 [false, true],
920 [false, true],
921 [false, true],
922 [false, true],
923 [false, true]
924 )]
925 fn test_initialize_vote_account(
926 bls_pubkey_management_in_vote_account: bool,
927 commission_rate_in_basis_points: bool,
928 custom_commission_collector: bool,
929 block_revenue_sharing: bool,
930 vote_account_initialize_v2: bool,
931 ) {
932 let vote_pubkey = solana_pubkey::new_rand();
933 let vote_account = AccountSharedData::new(100, vote_state_size_of(), &id());
934 let node_pubkey = solana_pubkey::new_rand();
935 let node_account = AccountSharedData::default();
936 let instruction_data = serialize(&VoteInstruction::InitializeAccount(VoteInit {
937 node_pubkey,
938 authorized_voter: vote_pubkey,
939 authorized_withdrawer: vote_pubkey,
940 commission: 0,
941 }))
942 .unwrap();
943 let mut instruction_accounts = vec![
944 AccountMeta {
945 pubkey: vote_pubkey,
946 is_signer: false,
947 is_writable: true,
948 },
949 AccountMeta {
950 pubkey: sysvar::rent::id(),
951 is_signer: false,
952 is_writable: false,
953 },
954 AccountMeta {
955 pubkey: sysvar::clock::id(),
956 is_signer: false,
957 is_writable: false,
958 },
959 AccountMeta {
960 pubkey: node_pubkey,
961 is_signer: true,
962 is_writable: false,
963 },
964 ];
965
966 let features = VoteProgramFeatures {
967 bls_pubkey_management_in_vote_account,
968 commission_rate_in_basis_points,
969 custom_commission_collector,
970 block_revenue_sharing,
971 vote_account_initialize_v2,
972 alpenglow_migration_succeeded: false,
973 };
974
975 let accounts = process_instruction(
976 features,
977 &instruction_data,
978 vec![
979 (vote_pubkey, vote_account.clone()),
980 (sysvar::rent::id(), create_default_rent_account()),
981 (sysvar::clock::id(), create_default_clock_account()),
982 (node_pubkey, node_account.clone()),
983 ],
984 instruction_accounts.clone(),
985 Ok(()),
986 );
987
988 process_instruction(
990 features,
991 &instruction_data,
992 vec![
993 (vote_pubkey, accounts[0].clone()),
994 (sysvar::rent::id(), create_default_rent_account()),
995 (sysvar::clock::id(), create_default_clock_account()),
996 (node_pubkey, accounts[3].clone()),
997 ],
998 instruction_accounts.clone(),
999 Err(InstructionError::AccountAlreadyInitialized),
1000 );
1001
1002 process_instruction(
1004 features,
1005 &instruction_data,
1006 vec![
1007 (
1008 vote_pubkey,
1009 AccountSharedData::new(100, 2 * vote_state_size_of(), &id()),
1010 ),
1011 (sysvar::rent::id(), create_default_rent_account()),
1012 (sysvar::clock::id(), create_default_clock_account()),
1013 (node_pubkey, node_account.clone()),
1014 ],
1015 instruction_accounts.clone(),
1016 Err(InstructionError::InvalidAccountData),
1017 );
1018
1019 instruction_accounts[3].is_signer = false;
1021 process_instruction(
1022 features,
1023 &instruction_data,
1024 vec![
1025 (vote_pubkey, vote_account),
1026 (sysvar::rent::id(), create_default_rent_account()),
1027 (sysvar::clock::id(), create_default_clock_account()),
1028 (node_pubkey, node_account),
1029 ],
1030 instruction_accounts.clone(),
1031 Err(InstructionError::MissingRequiredSignature),
1032 );
1033 }
1034
1035 #[test_matrix(
1036 [false, true],
1037 [false, true],
1038 [false, true],
1039 [false, true],
1040 [false, true]
1041 )]
1042 fn test_initialize_vote_account_v2(
1043 bls_pubkey_management_in_vote_account: bool,
1044 commission_rate_in_basis_points: bool,
1045 custom_commission_collector: bool,
1046 block_revenue_sharing: bool,
1047 vote_account_initialize_v2: bool,
1048 ) {
1049 let vote_pubkey = solana_pubkey::new_rand();
1050 let vote_account = AccountSharedData::new(100, vote_state_size_of(), &id());
1051 let node_pubkey = solana_pubkey::new_rand();
1052 let node_account = AccountSharedData::default();
1053 let authorized_voter = solana_pubkey::new_rand();
1054 let authorized_withdrawer = solana_pubkey::new_rand();
1055 let (bls_pubkey, bls_proof_of_possession) =
1056 create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
1057 let inflation_rewards_collector = solana_pubkey::new_rand();
1058 let inflation_rewards_collector_account =
1059 AccountSharedData::new(0, 0, &solana_sdk_ids::system_program::id());
1060 let block_revenue_collector = solana_pubkey::new_rand();
1061 let block_revenue_collector_account =
1062 AccountSharedData::new(0, 0, &solana_sdk_ids::system_program::id());
1063 let inflation_rewards_commission_bps = 1_234;
1064 let block_revenue_commission_bps = 5_678;
1065 let instruction_data = serialize(&VoteInstruction::InitializeAccountV2(VoteInitV2 {
1066 node_pubkey,
1067 authorized_voter,
1068 authorized_voter_bls_pubkey: bls_pubkey,
1069 authorized_voter_bls_proof_of_possession: bls_proof_of_possession,
1070 authorized_withdrawer,
1071 inflation_rewards_commission_bps,
1072 block_revenue_commission_bps,
1073 }))
1074 .unwrap();
1075 let mut instruction_accounts = vec![
1076 AccountMeta {
1077 pubkey: vote_pubkey,
1078 is_signer: false,
1079 is_writable: true,
1080 },
1081 AccountMeta {
1082 pubkey: node_pubkey,
1083 is_signer: true,
1084 is_writable: false,
1085 },
1086 AccountMeta {
1087 pubkey: inflation_rewards_collector,
1088 is_signer: false,
1089 is_writable: true,
1090 },
1091 AccountMeta {
1092 pubkey: block_revenue_collector,
1093 is_signer: false,
1094 is_writable: true,
1095 },
1096 ];
1097
1098 let features = VoteProgramFeatures {
1099 bls_pubkey_management_in_vote_account,
1100 commission_rate_in_basis_points,
1101 custom_commission_collector,
1102 block_revenue_sharing,
1103 vote_account_initialize_v2,
1104 alpenglow_migration_succeeded: false,
1105 };
1106
1107 let all_v2_features_enabled = bls_pubkey_management_in_vote_account
1108 && commission_rate_in_basis_points
1109 && custom_commission_collector
1110 && block_revenue_sharing
1111 && vote_account_initialize_v2;
1112
1113 if !all_v2_features_enabled {
1115 process_instruction(
1116 features,
1117 &instruction_data,
1118 vec![
1119 (vote_pubkey, vote_account),
1120 (node_pubkey, node_account),
1121 (
1122 inflation_rewards_collector,
1123 inflation_rewards_collector_account,
1124 ),
1125 (block_revenue_collector, block_revenue_collector_account),
1126 (sysvar::rent::id(), create_default_rent_account()),
1127 (sysvar::clock::id(), create_default_clock_account()),
1128 ],
1129 instruction_accounts.clone(),
1130 Err(InstructionError::InvalidInstructionData),
1131 );
1132 return;
1133 }
1134
1135 let assert_v4_fields =
1138 |vote_account: &AccountSharedData,
1139 expected_inflation_rewards_collector: Pubkey,
1140 expected_block_revenue_collector: Pubkey| {
1141 let v4 = deserialize_vote_state_for_test(vote_account.data(), &vote_pubkey);
1142 let v4 = v4.as_ref_v4();
1143 assert_eq!(v4.node_pubkey, node_pubkey);
1144 assert_eq!(v4.authorized_withdrawer, authorized_withdrawer);
1145 assert_eq!(v4.bls_pubkey_compressed, Some(bls_pubkey));
1146 assert_eq!(
1147 v4.inflation_rewards_commission_bps,
1148 inflation_rewards_commission_bps
1149 );
1150 assert_eq!(
1151 v4.inflation_rewards_collector,
1152 expected_inflation_rewards_collector
1153 );
1154 assert_eq!(
1155 v4.block_revenue_commission_bps,
1156 block_revenue_commission_bps
1157 );
1158 assert_eq!(v4.block_revenue_collector, expected_block_revenue_collector);
1159 assert_eq!(v4.pending_delegator_rewards, 0);
1160 assert!(v4.votes.is_empty());
1161 assert!(v4.epoch_credits.is_empty());
1162 assert_eq!(v4.root_slot, None);
1163 };
1164
1165 let accounts = process_instruction_with_cu_check(
1166 features,
1167 &instruction_data,
1168 vec![
1169 (vote_pubkey, vote_account.clone()),
1170 (node_pubkey, node_account.clone()),
1171 (
1172 inflation_rewards_collector,
1173 inflation_rewards_collector_account.clone(),
1174 ),
1175 (
1176 block_revenue_collector,
1177 block_revenue_collector_account.clone(),
1178 ),
1179 (sysvar::rent::id(), create_default_rent_account()),
1180 (sysvar::clock::id(), create_default_clock_account()),
1181 ],
1182 instruction_accounts.clone(),
1183 Ok(()),
1184 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
1185 );
1186 assert_v4_fields(
1187 &accounts[0],
1188 inflation_rewards_collector,
1189 block_revenue_collector,
1190 );
1191
1192 process_instruction(
1194 features,
1195 &instruction_data,
1196 vec![
1197 (vote_pubkey, accounts[0].clone()),
1198 (node_pubkey, accounts[1].clone()),
1199 (
1200 inflation_rewards_collector,
1201 inflation_rewards_collector_account.clone(),
1202 ),
1203 (
1204 block_revenue_collector,
1205 block_revenue_collector_account.clone(),
1206 ),
1207 (sysvar::rent::id(), create_default_rent_account()),
1208 (sysvar::clock::id(), create_default_clock_account()),
1209 ],
1210 instruction_accounts.clone(),
1211 Err(InstructionError::AccountAlreadyInitialized),
1212 );
1213
1214 process_instruction(
1216 features,
1217 &instruction_data,
1218 vec![
1219 (
1220 vote_pubkey,
1221 AccountSharedData::new(100, 2 * vote_state_size_of(), &id()),
1222 ),
1223 (node_pubkey, node_account.clone()),
1224 (
1225 inflation_rewards_collector,
1226 inflation_rewards_collector_account.clone(),
1227 ),
1228 (
1229 block_revenue_collector,
1230 block_revenue_collector_account.clone(),
1231 ),
1232 (sysvar::rent::id(), create_default_rent_account()),
1233 (sysvar::clock::id(), create_default_clock_account()),
1234 ],
1235 instruction_accounts.clone(),
1236 Err(InstructionError::InvalidAccountData),
1237 );
1238
1239 instruction_accounts[1].is_signer = false;
1241 process_instruction(
1242 features,
1243 &instruction_data,
1244 vec![
1245 (vote_pubkey, vote_account.clone()),
1246 (node_pubkey, node_account.clone()),
1247 (
1248 inflation_rewards_collector,
1249 inflation_rewards_collector_account.clone(),
1250 ),
1251 (
1252 block_revenue_collector,
1253 block_revenue_collector_account.clone(),
1254 ),
1255 (sysvar::rent::id(), create_default_rent_account()),
1256 (sysvar::clock::id(), create_default_clock_account()),
1257 ],
1258 instruction_accounts.clone(),
1259 Err(InstructionError::MissingRequiredSignature),
1260 );
1261 instruction_accounts[1].is_signer = true;
1262
1263 process_instruction(
1265 features,
1266 &instruction_data,
1267 vec![
1268 (vote_pubkey, vote_account.clone()),
1269 (node_pubkey, node_account.clone()),
1270 (
1271 inflation_rewards_collector,
1272 inflation_rewards_collector_account.clone(),
1273 ),
1274 (sysvar::rent::id(), create_default_rent_account()),
1275 (sysvar::clock::id(), create_default_clock_account()),
1276 ],
1277 instruction_accounts[..3].to_vec(),
1278 Err(InstructionError::MissingAccount),
1279 );
1280
1281 let mut aliased_instruction_accounts = instruction_accounts.clone();
1283 aliased_instruction_accounts[2].pubkey = vote_pubkey;
1284 aliased_instruction_accounts[3].pubkey = vote_pubkey;
1285 let accounts = process_instruction_with_cu_check(
1286 features,
1287 &instruction_data,
1288 vec![
1289 (vote_pubkey, vote_account.clone()),
1290 (node_pubkey, node_account.clone()),
1291 (sysvar::rent::id(), create_default_rent_account()),
1292 (sysvar::clock::id(), create_default_clock_account()),
1293 ],
1294 aliased_instruction_accounts,
1295 Ok(()),
1296 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
1297 );
1298 assert_v4_fields(&accounts[0], vote_pubkey, vote_pubkey);
1299
1300 let mut aliased_instruction_accounts = instruction_accounts.clone();
1303 aliased_instruction_accounts[2].pubkey = inflation_rewards_collector;
1304 aliased_instruction_accounts[3].pubkey = inflation_rewards_collector;
1305 let accounts = process_instruction_with_cu_check(
1306 features,
1307 &instruction_data,
1308 vec![
1309 (vote_pubkey, vote_account),
1310 (node_pubkey, node_account),
1311 (
1312 inflation_rewards_collector,
1313 inflation_rewards_collector_account,
1314 ),
1315 (sysvar::rent::id(), create_default_rent_account()),
1316 (sysvar::clock::id(), create_default_clock_account()),
1317 ],
1318 aliased_instruction_accounts,
1319 Ok(()),
1320 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
1321 );
1322 assert_v4_fields(
1323 &accounts[0],
1324 inflation_rewards_collector,
1325 inflation_rewards_collector,
1326 );
1327 }
1328
1329 #[test]
1330 fn test_initialize_vote_account_v2_bad_proof_of_possession() {
1331 let vote_pubkey = solana_pubkey::new_rand();
1332 let vote_account = AccountSharedData::new(100, VoteStateV4::size_of(), &id());
1333 let node_pubkey = solana_pubkey::new_rand();
1334 let node_account = AccountSharedData::default();
1335 let inflation_rewards_collector = solana_pubkey::new_rand();
1336 let inflation_rewards_collector_account =
1337 AccountSharedData::new(0, 0, &solana_sdk_ids::system_program::id());
1338 let block_revenue_collector = solana_pubkey::new_rand();
1339 let block_revenue_collector_account =
1340 AccountSharedData::new(0, 0, &solana_sdk_ids::system_program::id());
1341 let instruction_with_bad_pop =
1342 serialize(&VoteInstruction::InitializeAccountV2(VoteInitV2 {
1343 node_pubkey,
1344 authorized_voter: vote_pubkey,
1345 authorized_withdrawer: vote_pubkey,
1346 authorized_voter_bls_pubkey: [1u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE],
1347 authorized_voter_bls_proof_of_possession: [2u8;
1348 BLS_PROOF_OF_POSSESSION_COMPRESSED_SIZE],
1349 ..Default::default()
1350 }))
1351 .unwrap();
1352 let instruction_accounts = vec![
1353 AccountMeta {
1354 pubkey: vote_pubkey,
1355 is_signer: false,
1356 is_writable: true,
1357 },
1358 AccountMeta {
1359 pubkey: node_pubkey,
1360 is_signer: true,
1361 is_writable: false,
1362 },
1363 AccountMeta {
1364 pubkey: inflation_rewards_collector,
1365 is_signer: false,
1366 is_writable: true,
1367 },
1368 AccountMeta {
1369 pubkey: block_revenue_collector,
1370 is_signer: false,
1371 is_writable: true,
1372 },
1373 ];
1374 process_instruction_with_cu_check(
1375 VoteProgramFeatures::all_enabled(),
1376 &instruction_with_bad_pop,
1377 vec![
1378 (vote_pubkey, vote_account),
1379 (node_pubkey, node_account),
1380 (
1381 inflation_rewards_collector,
1382 inflation_rewards_collector_account.clone(),
1383 ),
1384 (
1385 block_revenue_collector,
1386 block_revenue_collector_account.clone(),
1387 ),
1388 (sysvar::rent::id(), create_default_rent_account()),
1389 (sysvar::clock::id(), create_default_clock_account()),
1390 ],
1391 instruction_accounts,
1392 Err(InstructionError::InvalidArgument),
1393 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
1394 );
1395 }
1396
1397 #[test_matrix([false, true])]
1398 fn test_vote_update_validator_identity(custom_commission_collector: bool) {
1399 let (vote_pubkey, _authorized_voter, authorized_withdrawer, vote_account) =
1400 create_test_account_with_authorized();
1401
1402 let original_block_revenue_collector = {
1403 let vote_state = deserialize_vote_state_for_test(vote_account.data(), &vote_pubkey);
1404 vote_state.as_ref_v4().block_revenue_collector
1405 };
1406
1407 let node_pubkey = solana_pubkey::new_rand();
1408 let instruction_data = serialize(&VoteInstruction::UpdateValidatorIdentity).unwrap();
1409 let transaction_accounts = vec![
1410 (vote_pubkey, vote_account),
1411 (node_pubkey, AccountSharedData::default()),
1412 (authorized_withdrawer, AccountSharedData::default()),
1413 ];
1414 let mut instruction_accounts = vec![
1415 AccountMeta {
1416 pubkey: vote_pubkey,
1417 is_signer: false,
1418 is_writable: true,
1419 },
1420 AccountMeta {
1421 pubkey: node_pubkey,
1422 is_signer: true,
1423 is_writable: false,
1424 },
1425 AccountMeta {
1426 pubkey: authorized_withdrawer,
1427 is_signer: true,
1428 is_writable: false,
1429 },
1430 ];
1431
1432 let features = VoteProgramFeatures {
1433 custom_commission_collector,
1434 ..Default::default()
1435 };
1436
1437 instruction_accounts[1].is_signer = false;
1439 let accounts = process_instruction(
1440 features,
1441 &instruction_data,
1442 transaction_accounts.clone(),
1443 instruction_accounts.clone(),
1444 Err(InstructionError::MissingRequiredSignature),
1445 );
1446 instruction_accounts[1].is_signer = true;
1447 let vote_state = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
1448 assert_ne!(*vote_state.node_pubkey(), node_pubkey);
1449
1450 instruction_accounts[2].is_signer = false;
1452 let accounts = process_instruction(
1453 features,
1454 &instruction_data,
1455 transaction_accounts.clone(),
1456 instruction_accounts.clone(),
1457 Err(InstructionError::MissingRequiredSignature),
1458 );
1459 instruction_accounts[2].is_signer = true;
1460 let vote_state = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
1461 assert_ne!(*vote_state.node_pubkey(), node_pubkey);
1462
1463 let accounts = process_instruction(
1465 features,
1466 &instruction_data,
1467 transaction_accounts,
1468 instruction_accounts,
1469 Ok(()),
1470 );
1471 let vote_state = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
1472 assert_eq!(*vote_state.node_pubkey(), node_pubkey);
1473 if custom_commission_collector {
1474 assert_eq!(
1477 vote_state.as_ref_v4().block_revenue_collector,
1478 original_block_revenue_collector,
1479 );
1480 } else {
1481 assert_eq!(vote_state.as_ref_v4().block_revenue_collector, node_pubkey);
1484 }
1485 }
1486
1487 #[test]
1488 fn test_vote_update_commission() {
1489 let (vote_pubkey, _authorized_voter, authorized_withdrawer, vote_account) =
1490 create_test_account_with_authorized();
1491 let instruction_data = serialize(&VoteInstruction::UpdateCommission(42)).unwrap();
1492 let transaction_accounts = vec![
1493 (vote_pubkey, vote_account),
1494 (authorized_withdrawer, AccountSharedData::default()),
1495 (
1497 sysvar::clock::id(),
1498 create_sysvar_account(&Clock::default()),
1499 ),
1500 (
1501 sysvar::epoch_schedule::id(),
1502 create_sysvar_account(&EpochSchedule::without_warmup()),
1503 ),
1504 ];
1505 let mut instruction_accounts = vec![
1506 AccountMeta {
1507 pubkey: vote_pubkey,
1508 is_signer: false,
1509 is_writable: true,
1510 },
1511 AccountMeta {
1512 pubkey: authorized_withdrawer,
1513 is_signer: true,
1514 is_writable: false,
1515 },
1516 ];
1517
1518 let features = VoteProgramFeatures {
1519 ..Default::default()
1520 };
1521
1522 let accounts = process_instruction(
1524 features,
1525 &serialize(&VoteInstruction::UpdateCommission(200)).unwrap(),
1526 transaction_accounts.clone(),
1527 instruction_accounts.clone(),
1528 Ok(()),
1529 );
1530 let vote_state = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
1531 assert_eq!(vote_state.commission(), 200);
1532
1533 let accounts = process_instruction(
1535 features,
1536 &instruction_data,
1537 transaction_accounts.clone(),
1538 instruction_accounts.clone(),
1539 Ok(()),
1540 );
1541 let vote_state = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
1542 assert_eq!(vote_state.commission(), 42);
1543
1544 instruction_accounts[1].is_signer = false;
1546 let accounts = process_instruction(
1547 features,
1548 &instruction_data,
1549 transaction_accounts,
1550 instruction_accounts,
1551 Err(InstructionError::MissingRequiredSignature),
1552 );
1553 let vote_state = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
1554 assert_eq!(vote_state.commission(), 0);
1555 }
1556
1557 #[test]
1558 fn test_vote_update_commission_bps() {
1559 let (vote_pubkey, _authorized_voter, authorized_withdrawer, vote_account) =
1561 create_test_account_with_authorized();
1562
1563 let transaction_accounts = vec![
1564 (vote_pubkey, vote_account.clone()),
1565 (authorized_withdrawer, AccountSharedData::default()),
1566 ];
1567
1568 let instruction_accounts = vec![
1569 AccountMeta {
1570 pubkey: vote_pubkey,
1571 is_signer: false,
1572 is_writable: true,
1573 },
1574 AccountMeta {
1575 pubkey: authorized_withdrawer,
1576 is_signer: true,
1577 is_writable: false,
1578 },
1579 ];
1580
1581 let features = VoteProgramFeatures::all_enabled();
1582
1583 let get_commission_bps = |vote_account: &AccountSharedData, kind: &CommissionKind| {
1584 let vote_state = deserialize_vote_state_for_test(vote_account.data(), &vote_pubkey);
1585 match kind {
1586 CommissionKind::InflationRewards => {
1587 vote_state.as_ref_v4().inflation_rewards_commission_bps
1588 }
1589 CommissionKind::BlockRevenue => vote_state.as_ref_v4().block_revenue_commission_bps,
1590 }
1591 };
1592
1593 let original_commission_bps =
1594 get_commission_bps(&vote_account, &CommissionKind::InflationRewards);
1595
1596 let commission_bps = 200; for kind in [
1599 CommissionKind::InflationRewards,
1600 CommissionKind::BlockRevenue,
1601 ] {
1602 let other_kind = match kind {
1603 CommissionKind::InflationRewards => CommissionKind::BlockRevenue,
1604 CommissionKind::BlockRevenue => CommissionKind::InflationRewards,
1605 };
1606
1607 let original_other_commission_bps = get_commission_bps(&vote_account, &other_kind);
1609
1610 let instruction_data = serialize(&VoteInstruction::UpdateCommissionBps {
1611 commission_bps,
1612 kind: kind.clone(),
1613 })
1614 .unwrap();
1615
1616 let accounts = process_instruction(
1618 features,
1619 &instruction_data,
1620 transaction_accounts.clone(),
1621 instruction_accounts.clone(),
1622 Ok(()),
1623 );
1624 assert_eq!(get_commission_bps(&accounts[0], &kind), commission_bps);
1625
1626 assert_eq!(
1628 get_commission_bps(&accounts[0], &other_kind),
1629 original_other_commission_bps,
1630 );
1631
1632 let accounts = process_instruction(
1634 features,
1635 &instruction_data,
1636 vec![
1637 (vote_pubkey, accounts[0].clone()),
1638 (authorized_withdrawer, accounts[1].clone()),
1639 ],
1640 instruction_accounts.clone(),
1641 Ok(()),
1642 );
1643 assert_eq!(get_commission_bps(&accounts[0], &kind), commission_bps);
1644
1645 assert_eq!(
1647 get_commission_bps(&accounts[0], &other_kind),
1648 original_other_commission_bps,
1649 );
1650 }
1651
1652 let instruction_data = serialize(&VoteInstruction::UpdateCommissionBps {
1653 commission_bps,
1654 kind: CommissionKind::InflationRewards,
1655 })
1656 .unwrap();
1657
1658 let accounts = process_instruction(
1660 VoteProgramFeatures {
1661 block_revenue_sharing: false,
1662 ..features
1663 },
1664 &serialize(&VoteInstruction::UpdateCommissionBps {
1665 commission_bps,
1666 kind: CommissionKind::BlockRevenue,
1667 })
1668 .unwrap(),
1669 transaction_accounts.clone(),
1670 instruction_accounts.clone(),
1671 Err(InstructionError::InvalidInstructionData),
1672 );
1673 let stored_commission_bps = get_commission_bps(&accounts[0], &CommissionKind::BlockRevenue);
1674 assert_eq!(stored_commission_bps, 0); assert_ne!(stored_commission_bps, commission_bps); let mut unsigned_instruction_accounts = instruction_accounts;
1679 unsigned_instruction_accounts[1].is_signer = false;
1680 let accounts = process_instruction(
1681 features,
1682 &instruction_data,
1683 transaction_accounts.clone(),
1684 unsigned_instruction_accounts,
1685 Err(InstructionError::MissingRequiredSignature),
1686 );
1687 let stored_commission_bps =
1688 get_commission_bps(&accounts[0], &CommissionKind::InflationRewards);
1689 assert_eq!(stored_commission_bps, original_commission_bps); assert_ne!(stored_commission_bps, commission_bps); let wrong_signer = Pubkey::new_unique();
1694 let mut wrong_signer_transaction_accounts = transaction_accounts;
1695 wrong_signer_transaction_accounts.push((wrong_signer, AccountSharedData::default()));
1696 let wrong_signer_instruction_accounts = vec![
1697 AccountMeta {
1698 pubkey: vote_pubkey,
1699 is_signer: false,
1700 is_writable: true,
1701 },
1702 AccountMeta {
1703 pubkey: wrong_signer,
1704 is_signer: true,
1705 is_writable: false,
1706 },
1707 ];
1708 let accounts = process_instruction(
1709 features,
1710 &instruction_data,
1711 wrong_signer_transaction_accounts,
1712 wrong_signer_instruction_accounts,
1713 Err(InstructionError::MissingRequiredSignature),
1714 );
1715 let stored_commission_bps =
1716 get_commission_bps(&accounts[0], &CommissionKind::InflationRewards);
1717 assert_eq!(stored_commission_bps, original_commission_bps); assert_ne!(stored_commission_bps, commission_bps); }
1720
1721 #[test]
1722 fn test_vote_update_commission_collector() {
1723 let custom_commission_collector = true;
1725
1726 let (vote_pubkey, _authorized_voter, authorized_withdrawer, vote_account) =
1727 create_test_account_with_authorized();
1728
1729 let new_collector_pubkey = Pubkey::new_unique();
1731 let rent = Rent::default();
1732 let rent_sysvar_account = create_sysvar_account(&rent);
1733 let collector_lamports = rent.minimum_balance(0);
1734 let new_collector_account =
1735 AccountSharedData::new(collector_lamports, 0, &solana_sdk_ids::system_program::id());
1736
1737 let transaction_accounts = vec![
1738 (vote_pubkey, vote_account.clone()),
1739 (new_collector_pubkey, new_collector_account),
1740 (authorized_withdrawer, AccountSharedData::default()),
1741 (sysvar::rent::id(), rent_sysvar_account),
1742 ];
1743
1744 let instruction_accounts = vec![
1745 AccountMeta {
1746 pubkey: vote_pubkey,
1747 is_signer: false,
1748 is_writable: true,
1749 },
1750 AccountMeta {
1751 pubkey: new_collector_pubkey,
1752 is_signer: false,
1753 is_writable: true,
1754 },
1755 AccountMeta {
1756 pubkey: authorized_withdrawer,
1757 is_signer: true,
1758 is_writable: false,
1759 },
1760 ];
1761
1762 let features = VoteProgramFeatures {
1763 custom_commission_collector,
1764 ..Default::default()
1765 };
1766
1767 let get_commission_collector = |vote_account: &AccountSharedData, kind: CommissionKind| {
1768 let vote_state = deserialize_vote_state_for_test(vote_account.data(), &vote_pubkey)
1769 .as_ref_v4()
1770 .clone();
1771 match kind {
1772 CommissionKind::InflationRewards => vote_state.inflation_rewards_collector,
1773 CommissionKind::BlockRevenue => vote_state.block_revenue_collector,
1774 }
1775 };
1776
1777 let original_inflation_collector =
1778 get_commission_collector(&vote_account, CommissionKind::InflationRewards);
1779 let original_block_revenue_collector =
1780 get_commission_collector(&vote_account, CommissionKind::BlockRevenue);
1781
1782 let instruction_data = serialize(&VoteInstruction::UpdateCommissionCollector(
1784 CommissionKind::InflationRewards,
1785 ))
1786 .unwrap();
1787 let accounts = process_instruction(
1788 features,
1789 &instruction_data,
1790 transaction_accounts.clone(),
1791 instruction_accounts.clone(),
1792 Ok(()),
1793 );
1794 assert_eq!(
1795 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
1796 new_collector_pubkey,
1797 );
1798 assert_eq!(
1799 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
1800 original_block_revenue_collector, );
1802
1803 let instruction_data = serialize(&VoteInstruction::UpdateCommissionCollector(
1805 CommissionKind::BlockRevenue,
1806 ))
1807 .unwrap();
1808 let accounts = process_instruction(
1809 features,
1810 &instruction_data,
1811 transaction_accounts.clone(),
1812 instruction_accounts.clone(),
1813 Ok(()),
1814 );
1815 assert_eq!(
1816 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
1817 original_inflation_collector, );
1819 assert_eq!(
1820 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
1821 new_collector_pubkey,
1822 );
1823
1824 let vote_as_collector_instruction_accounts = vec![
1826 AccountMeta {
1827 pubkey: vote_pubkey,
1828 is_signer: false,
1829 is_writable: true,
1830 },
1831 AccountMeta {
1832 pubkey: vote_pubkey, is_signer: false,
1834 is_writable: true,
1835 },
1836 AccountMeta {
1837 pubkey: authorized_withdrawer,
1838 is_signer: true,
1839 is_writable: false,
1840 },
1841 ];
1842 let instruction_data = serialize(&VoteInstruction::UpdateCommissionCollector(
1843 CommissionKind::InflationRewards,
1844 ))
1845 .unwrap();
1846 let accounts = process_instruction(
1847 features,
1848 &instruction_data,
1849 transaction_accounts.clone(),
1850 vote_as_collector_instruction_accounts.clone(),
1851 Ok(()),
1852 );
1853 assert_eq!(
1854 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
1855 vote_pubkey
1856 );
1857 assert_eq!(
1858 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
1859 original_block_revenue_collector, );
1861
1862 let instruction_data = serialize(&VoteInstruction::UpdateCommissionCollector(
1864 CommissionKind::BlockRevenue,
1865 ))
1866 .unwrap();
1867 let accounts = process_instruction(
1868 features,
1869 &instruction_data,
1870 transaction_accounts.clone(),
1871 vote_as_collector_instruction_accounts,
1872 Ok(()),
1873 );
1874 assert_eq!(
1875 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
1876 original_inflation_collector, );
1878 assert_eq!(
1879 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
1880 vote_pubkey
1881 );
1882
1883 let aliased_vote_account =
1885 create_test_account_with_provided_authorized(&vote_pubkey, &vote_pubkey);
1886 let aliased_original_inflation_collector =
1887 get_commission_collector(&aliased_vote_account, CommissionKind::InflationRewards);
1888 let aliased_original_block_revenue_collector =
1889 get_commission_collector(&aliased_vote_account, CommissionKind::BlockRevenue);
1890 let aliased_transaction_accounts = vec![
1891 (vote_pubkey, aliased_vote_account),
1892 (sysvar::rent::id(), create_sysvar_account(&rent)),
1893 ];
1894 let aliased_instruction_accounts = vec![
1895 AccountMeta {
1896 pubkey: vote_pubkey, is_signer: false,
1898 is_writable: true,
1899 },
1900 AccountMeta {
1901 pubkey: vote_pubkey, is_signer: false,
1903 is_writable: true,
1904 },
1905 AccountMeta {
1906 pubkey: vote_pubkey, is_signer: true, is_writable: false,
1909 },
1910 ];
1911
1912 let instruction_data = serialize(&VoteInstruction::UpdateCommissionCollector(
1914 CommissionKind::InflationRewards,
1915 ))
1916 .unwrap();
1917 let accounts = process_instruction(
1918 features,
1919 &instruction_data,
1920 aliased_transaction_accounts.clone(),
1921 aliased_instruction_accounts.clone(),
1922 Ok(()),
1923 );
1924 assert_eq!(
1925 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
1926 vote_pubkey
1927 );
1928 assert_eq!(
1929 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
1930 aliased_original_block_revenue_collector, );
1932
1933 let instruction_data = serialize(&VoteInstruction::UpdateCommissionCollector(
1935 CommissionKind::BlockRevenue,
1936 ))
1937 .unwrap();
1938 let accounts = process_instruction(
1939 features,
1940 &instruction_data,
1941 aliased_transaction_accounts,
1942 aliased_instruction_accounts,
1943 Ok(()),
1944 );
1945 assert_eq!(
1946 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
1947 aliased_original_inflation_collector, );
1949 assert_eq!(
1950 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
1951 vote_pubkey
1952 );
1953
1954 let instruction_data = serialize(&VoteInstruction::UpdateCommissionCollector(
1956 CommissionKind::InflationRewards,
1957 ))
1958 .unwrap();
1959 let accounts = process_instruction(
1960 VoteProgramFeatures {
1961 custom_commission_collector: false,
1962 ..Default::default()
1963 },
1964 &instruction_data,
1965 transaction_accounts.clone(),
1966 instruction_accounts.clone(),
1967 Err(InstructionError::InvalidInstructionData),
1968 );
1969 assert_eq!(
1970 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
1971 original_inflation_collector, );
1973 assert_eq!(
1974 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
1975 original_block_revenue_collector, );
1977
1978 let too_few_instruction_accounts = vec![AccountMeta {
1980 pubkey: vote_pubkey,
1981 is_signer: false,
1982 is_writable: true,
1983 }];
1984 let accounts = process_instruction(
1985 features,
1986 &instruction_data,
1987 transaction_accounts.clone(),
1988 too_few_instruction_accounts,
1989 Err(InstructionError::MissingAccount),
1990 );
1991 assert_eq!(
1992 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
1993 original_inflation_collector, );
1995 assert_eq!(
1996 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
1997 original_block_revenue_collector, );
1999
2000 let mut unsigned_instruction_accounts = instruction_accounts.clone();
2002 unsigned_instruction_accounts[2].is_signer = false;
2003 let accounts = process_instruction(
2004 features,
2005 &instruction_data,
2006 transaction_accounts.clone(),
2007 unsigned_instruction_accounts,
2008 Err(InstructionError::MissingRequiredSignature),
2009 );
2010 assert_eq!(
2011 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
2012 original_inflation_collector
2013 );
2014 assert_eq!(
2015 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
2016 original_block_revenue_collector, );
2018
2019 let wrong_signer = Pubkey::new_unique();
2021 let mut wrong_signer_transaction_accounts = transaction_accounts.clone();
2022 wrong_signer_transaction_accounts.push((wrong_signer, AccountSharedData::default()));
2023 let wrong_signer_instruction_accounts = vec![
2024 AccountMeta {
2025 pubkey: vote_pubkey,
2026 is_signer: false,
2027 is_writable: true,
2028 },
2029 AccountMeta {
2030 pubkey: new_collector_pubkey,
2031 is_signer: false,
2032 is_writable: true,
2033 },
2034 AccountMeta {
2035 pubkey: wrong_signer,
2036 is_signer: true,
2037 is_writable: false,
2038 },
2039 ];
2040 let accounts = process_instruction(
2041 features,
2042 &instruction_data,
2043 wrong_signer_transaction_accounts,
2044 wrong_signer_instruction_accounts,
2045 Err(InstructionError::MissingRequiredSignature),
2046 );
2047 assert_eq!(
2048 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
2049 original_inflation_collector
2050 );
2051 assert_eq!(
2052 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
2053 original_block_revenue_collector, );
2055
2056 let non_system_owner = Pubkey::new_unique();
2058 let non_system_collector_pubkey = Pubkey::new_unique();
2059 let non_system_collector_account =
2060 AccountSharedData::new(collector_lamports, 0, &non_system_owner);
2061 let mut non_system_transaction_accounts = transaction_accounts.clone();
2062 non_system_transaction_accounts[1] =
2063 (non_system_collector_pubkey, non_system_collector_account);
2064 let non_system_instruction_accounts = vec![
2065 AccountMeta {
2066 pubkey: vote_pubkey,
2067 is_signer: false,
2068 is_writable: true,
2069 },
2070 AccountMeta {
2071 pubkey: non_system_collector_pubkey,
2072 is_signer: false,
2073 is_writable: true,
2074 },
2075 AccountMeta {
2076 pubkey: authorized_withdrawer,
2077 is_signer: true,
2078 is_writable: false,
2079 },
2080 ];
2081 let accounts = process_instruction(
2082 features,
2083 &instruction_data,
2084 non_system_transaction_accounts,
2085 non_system_instruction_accounts,
2086 Err(InstructionError::InvalidAccountOwner),
2087 );
2088 assert_eq!(
2089 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
2090 original_inflation_collector
2091 );
2092 assert_eq!(
2093 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
2094 original_block_revenue_collector, );
2096
2097 let not_rent_exempt_collector_pubkey = Pubkey::new_unique();
2099 let not_rent_exempt_collector_account =
2100 AccountSharedData::new(0, 0, &solana_sdk_ids::system_program::id()); let mut not_rent_exempt_transaction_accounts = transaction_accounts.clone();
2102 not_rent_exempt_transaction_accounts[1] = (
2103 not_rent_exempt_collector_pubkey,
2104 not_rent_exempt_collector_account,
2105 );
2106 let not_rent_exempt_instruction_accounts = vec![
2107 AccountMeta {
2108 pubkey: vote_pubkey,
2109 is_signer: false,
2110 is_writable: true,
2111 },
2112 AccountMeta {
2113 pubkey: not_rent_exempt_collector_pubkey,
2114 is_signer: false,
2115 is_writable: true,
2116 },
2117 AccountMeta {
2118 pubkey: authorized_withdrawer,
2119 is_signer: true,
2120 is_writable: false,
2121 },
2122 ];
2123 let accounts = process_instruction(
2124 features,
2125 &instruction_data,
2126 not_rent_exempt_transaction_accounts,
2127 not_rent_exempt_instruction_accounts,
2128 Err(InstructionError::InsufficientFunds),
2129 );
2130 assert_eq!(
2131 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
2132 original_inflation_collector
2133 );
2134 assert_eq!(
2135 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
2136 original_block_revenue_collector, );
2138
2139 let mut not_writable_instruction_accounts = instruction_accounts;
2141 not_writable_instruction_accounts[1].is_writable = false;
2142 let accounts = process_instruction(
2143 features,
2144 &instruction_data,
2145 transaction_accounts,
2146 not_writable_instruction_accounts,
2147 Err(InstructionError::InvalidArgument),
2148 );
2149 assert_eq!(
2150 get_commission_collector(&accounts[0], CommissionKind::InflationRewards),
2151 original_inflation_collector
2152 );
2153 assert_eq!(
2154 get_commission_collector(&accounts[0], CommissionKind::BlockRevenue),
2155 original_block_revenue_collector, );
2157 }
2158
2159 #[test]
2160 fn test_vote_signature() {
2161 let (vote_pubkey, vote_account) = create_test_account();
2162 let (vote, instruction_datas) = create_serialized_votes();
2163 let slot_hashes = SlotHashes::new(&[(*vote.slots.last().unwrap(), vote.hash)]);
2164 let slot_hashes_account = create_sysvar_account(&slot_hashes);
2165 let mut instruction_accounts = vec![
2166 AccountMeta {
2167 pubkey: vote_pubkey,
2168 is_signer: true,
2169 is_writable: true,
2170 },
2171 AccountMeta {
2172 pubkey: sysvar::slot_hashes::id(),
2173 is_signer: false,
2174 is_writable: false,
2175 },
2176 AccountMeta {
2177 pubkey: sysvar::clock::id(),
2178 is_signer: false,
2179 is_writable: false,
2180 },
2181 ];
2182
2183 let features = VoteProgramFeatures {
2184 ..Default::default()
2185 };
2186
2187 for (instruction_data, is_tower_sync) in instruction_datas {
2188 let mut transaction_accounts = vec![
2189 (vote_pubkey, vote_account.clone()),
2190 (sysvar::slot_hashes::id(), slot_hashes_account.clone()),
2191 (sysvar::clock::id(), create_default_clock_account()),
2192 ];
2193
2194 let error = |err| {
2195 if !is_tower_sync {
2196 Err(InstructionError::InvalidInstructionData)
2197 } else {
2198 Err(err)
2199 }
2200 };
2201
2202 instruction_accounts[0].is_signer = false;
2204 process_instruction(
2205 features,
2206 &instruction_data,
2207 transaction_accounts.clone(),
2208 instruction_accounts.clone(),
2209 error(InstructionError::MissingRequiredSignature),
2210 );
2211 instruction_accounts[0].is_signer = true;
2212
2213 let accounts = process_instruction(
2215 features,
2216 &instruction_data,
2217 transaction_accounts.clone(),
2218 instruction_accounts.clone(),
2219 if is_tower_sync {
2220 Ok(())
2221 } else {
2222 Err(InstructionError::InvalidInstructionData)
2223 },
2224 );
2225 if is_tower_sync {
2226 let vote_state = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
2227 let expected_lockout = Lockout::new(*vote.slots.last().unwrap());
2228 assert_eq!(vote_state.votes().len(), 1);
2229 assert_eq!(vote_state.votes()[0].lockout, expected_lockout);
2230 assert_eq!(vote_state.credits(), 0);
2231 }
2232
2233 transaction_accounts[1] = (
2235 sysvar::slot_hashes::id(),
2236 create_sysvar_account(&SlotHashes::new(&[(
2237 *vote.slots.last().unwrap(),
2238 solana_sha256_hasher::hash(&[0u8]),
2239 )])),
2240 );
2241 process_instruction(
2242 features,
2243 &instruction_data,
2244 transaction_accounts.clone(),
2245 instruction_accounts.clone(),
2246 error(VoteError::SlotHashMismatch.into()),
2247 );
2248
2249 transaction_accounts[1] = (
2251 sysvar::slot_hashes::id(),
2252 create_sysvar_account(&SlotHashes::new(&[(0, vote.hash)])),
2253 );
2254 process_instruction(
2255 features,
2256 &instruction_data,
2257 transaction_accounts.clone(),
2258 instruction_accounts.clone(),
2259 error(VoteError::SlotsMismatch.into()),
2260 );
2261
2262 transaction_accounts[1] = (
2264 sysvar::slot_hashes::id(),
2265 create_sysvar_account(&SlotHashes::new(&[])),
2266 );
2267 process_instruction(
2268 features,
2269 &instruction_data,
2270 transaction_accounts.clone(),
2271 instruction_accounts.clone(),
2272 error(VoteError::SlotsMismatch.into()),
2273 );
2274 transaction_accounts[1] = (sysvar::slot_hashes::id(), slot_hashes_account.clone());
2275
2276 let vote_account = AccountSharedData::new(100, vote_state_size_of(), &id());
2278 transaction_accounts[0] = (vote_pubkey, vote_account);
2279 process_instruction(
2280 features,
2281 &instruction_data,
2282 transaction_accounts.clone(),
2283 instruction_accounts.clone(),
2284 error(InstructionError::InvalidAccountData),
2285 );
2286 }
2287 }
2288
2289 #[test_matrix([false, true])]
2290 fn test_authorize_voter(bls_pubkey_management_in_vote_account: bool) {
2291 let (vote_pubkey, vote_account) = create_test_account();
2292 let authorized_voter_pubkey = solana_pubkey::new_rand();
2293 let clock = Clock {
2294 epoch: 1,
2295 leader_schedule_epoch: 2,
2296 ..Clock::default()
2297 };
2298 let clock_account = create_sysvar_account(&clock);
2299 let instruction_data = serialize(&VoteInstruction::Authorize(
2300 authorized_voter_pubkey,
2301 VoteAuthorize::Voter,
2302 ))
2303 .unwrap();
2304
2305 let mut transaction_accounts = vec![
2306 (vote_pubkey, vote_account.clone()),
2307 (sysvar::clock::id(), clock_account.clone()),
2308 (authorized_voter_pubkey, AccountSharedData::default()),
2309 ];
2310 let mut instruction_accounts = vec![
2311 AccountMeta {
2312 pubkey: vote_pubkey,
2313 is_signer: true,
2314 is_writable: true,
2315 },
2316 AccountMeta {
2317 pubkey: sysvar::clock::id(),
2318 is_signer: false,
2319 is_writable: false,
2320 },
2321 ];
2322
2323 let features = VoteProgramFeatures {
2324 bls_pubkey_management_in_vote_account,
2325 ..Default::default()
2326 };
2327
2328 if bls_pubkey_management_in_vote_account {
2330 process_instruction(
2332 features,
2333 &instruction_data,
2334 vec![
2335 (vote_pubkey, vote_account),
2336 (sysvar::clock::id(), clock_account),
2337 (authorized_voter_pubkey, AccountSharedData::default()),
2338 ],
2339 instruction_accounts.clone(),
2340 Err(InstructionError::InvalidInstructionData),
2341 );
2342 return;
2343 } else {
2344 let (bls_pubkey, bls_proof_of_possession) =
2346 create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
2347 let bad_instruction_data = serialize(&VoteInstruction::Authorize(
2348 authorized_voter_pubkey,
2349 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
2350 bls_pubkey,
2351 bls_proof_of_possession,
2352 }),
2353 ))
2354 .unwrap();
2355 process_instruction(
2356 features,
2357 &bad_instruction_data,
2358 vec![
2359 (vote_pubkey, vote_account),
2360 (sysvar::clock::id(), clock_account),
2361 (authorized_voter_pubkey, AccountSharedData::default()),
2362 ],
2363 instruction_accounts.clone(),
2364 Err(InstructionError::InvalidInstructionData),
2365 );
2366 }
2367
2368 instruction_accounts[0].is_signer = false;
2370 process_instruction(
2371 features,
2372 &instruction_data,
2373 transaction_accounts.clone(),
2374 instruction_accounts.clone(),
2375 Err(InstructionError::MissingRequiredSignature),
2376 );
2377 instruction_accounts[0].is_signer = true;
2378
2379 let accounts = process_instruction(
2381 features,
2382 &instruction_data,
2383 transaction_accounts.clone(),
2384 instruction_accounts.clone(),
2385 Ok(()),
2386 );
2387
2388 transaction_accounts[0] = (vote_pubkey, accounts[0].clone());
2390 process_instruction(
2391 features,
2392 &instruction_data,
2393 transaction_accounts.clone(),
2394 instruction_accounts.clone(),
2395 Err(VoteError::TooSoonToReauthorize.into()),
2396 );
2397
2398 instruction_accounts[0].is_signer = false;
2400 instruction_accounts.push(AccountMeta {
2401 pubkey: authorized_voter_pubkey,
2402 is_signer: true,
2403 is_writable: false,
2404 });
2405 let clock = Clock {
2406 epoch: 3,
2409 leader_schedule_epoch: 4,
2410 ..Clock::default()
2411 };
2412 let clock_account = create_sysvar_account(&clock);
2413 transaction_accounts[1] = (sysvar::clock::id(), clock_account);
2414 process_instruction(
2415 features,
2416 &instruction_data,
2417 transaction_accounts.clone(),
2418 instruction_accounts.clone(),
2419 Ok(()),
2420 );
2421 instruction_accounts[0].is_signer = true;
2422 instruction_accounts.pop();
2423
2424 let (vote, instruction_datas) = create_serialized_votes();
2426 let slot_hashes = SlotHashes::new(&[(*vote.slots.last().unwrap(), vote.hash)]);
2427 let slot_hashes_account = create_sysvar_account(&slot_hashes);
2428 transaction_accounts.push((sysvar::slot_hashes::id(), slot_hashes_account));
2429 instruction_accounts.insert(
2430 1,
2431 AccountMeta {
2432 pubkey: sysvar::slot_hashes::id(),
2433 is_signer: false,
2434 is_writable: false,
2435 },
2436 );
2437 let mut authorized_instruction_accounts = instruction_accounts.clone();
2438 authorized_instruction_accounts.push(AccountMeta {
2439 pubkey: authorized_voter_pubkey,
2440 is_signer: true,
2441 is_writable: false,
2442 });
2443
2444 for (instruction_data, is_tower_sync) in instruction_datas {
2445 process_instruction(
2446 features,
2447 &instruction_data,
2448 transaction_accounts.clone(),
2449 instruction_accounts.clone(),
2450 Err(if is_tower_sync {
2451 InstructionError::MissingRequiredSignature
2452 } else {
2453 InstructionError::InvalidInstructionData
2454 }),
2455 );
2456
2457 process_instruction(
2459 features,
2460 &instruction_data,
2461 transaction_accounts.clone(),
2462 authorized_instruction_accounts.clone(),
2463 if is_tower_sync {
2464 Ok(())
2465 } else {
2466 Err(InstructionError::InvalidInstructionData)
2467 },
2468 );
2469 }
2470 }
2471
2472 #[test_matrix([false, true])]
2473 fn test_authorize_voter_with_bls(bls_pubkey_management_in_vote_account: bool) {
2474 agave_logger::setup();
2475 let (vote_pubkey, vote_account) = create_test_account();
2476 let authorized_voter_pubkey = solana_pubkey::new_rand();
2477 let clock = Clock {
2478 epoch: 1,
2479 leader_schedule_epoch: 2,
2480 ..Clock::default()
2481 };
2482 let clock_account = create_sysvar_account(&clock);
2483 let (bls_pubkey, bls_proof_of_possession) =
2484 create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
2485 let instruction_data = serialize(&VoteInstruction::Authorize(
2486 authorized_voter_pubkey,
2487 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
2488 bls_pubkey,
2489 bls_proof_of_possession,
2490 }),
2491 ))
2492 .unwrap();
2493
2494 let mut transaction_accounts = vec![
2495 (vote_pubkey, vote_account.clone()),
2496 (sysvar::clock::id(), clock_account.clone()),
2497 (authorized_voter_pubkey, AccountSharedData::default()),
2498 ];
2499 let mut instruction_accounts = vec![
2500 AccountMeta {
2501 pubkey: vote_pubkey,
2502 is_signer: true,
2503 is_writable: true,
2504 },
2505 AccountMeta {
2506 pubkey: sysvar::clock::id(),
2507 is_signer: false,
2508 is_writable: false,
2509 },
2510 ];
2511
2512 let features = VoteProgramFeatures {
2513 bls_pubkey_management_in_vote_account,
2514 ..Default::default()
2515 };
2516
2517 if bls_pubkey_management_in_vote_account {
2519 let (new_vote_pubkey, vote_account_no_bls_key) = create_test_account_no_bls_key();
2522 let new_authorized_voter_pubkey = solana_pubkey::new_rand();
2523 let old_instruction_data = serialize(&VoteInstruction::Authorize(
2524 new_authorized_voter_pubkey,
2525 VoteAuthorize::Voter,
2526 ))
2527 .unwrap();
2528 process_instruction(
2529 features,
2530 &old_instruction_data,
2531 vec![
2532 (new_vote_pubkey, vote_account_no_bls_key),
2533 (sysvar::clock::id(), clock_account.clone()),
2534 (new_authorized_voter_pubkey, AccountSharedData::default()),
2535 ],
2536 vec![
2537 AccountMeta {
2538 pubkey: new_vote_pubkey,
2539 is_signer: true,
2540 is_writable: true,
2541 },
2542 AccountMeta {
2543 pubkey: sysvar::clock::id(),
2544 is_signer: false,
2545 is_writable: false,
2546 },
2547 ],
2548 Ok(()),
2549 );
2550 let (new_vote_pubkey, vote_account_with_bls_key) = create_test_account();
2552 let new_authorized_voter_pubkey = solana_pubkey::new_rand();
2553 let old_instruction_data = serialize(&VoteInstruction::Authorize(
2554 new_authorized_voter_pubkey,
2555 VoteAuthorize::Voter,
2556 ))
2557 .unwrap();
2558 process_instruction(
2559 features,
2560 &old_instruction_data,
2561 vec![
2562 (new_vote_pubkey, vote_account_with_bls_key),
2563 (sysvar::clock::id(), clock_account),
2564 (new_authorized_voter_pubkey, AccountSharedData::default()),
2565 ],
2566 vec![
2567 AccountMeta {
2568 pubkey: new_vote_pubkey,
2569 is_signer: true,
2570 is_writable: true,
2571 },
2572 AccountMeta {
2573 pubkey: sysvar::clock::id(),
2574 is_signer: false,
2575 is_writable: false,
2576 },
2577 ],
2578 Err(InstructionError::InvalidInstructionData),
2579 );
2580 } else {
2581 let (bls_pubkey, bls_proof_of_possession) =
2583 create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
2584 let bad_instruction_data = serialize(&VoteInstruction::Authorize(
2585 authorized_voter_pubkey,
2586 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
2587 bls_pubkey,
2588 bls_proof_of_possession,
2589 }),
2590 ))
2591 .unwrap();
2592
2593 process_instruction(
2594 features,
2595 &bad_instruction_data,
2596 vec![
2597 (vote_pubkey, vote_account),
2598 (sysvar::clock::id(), clock_account),
2599 (authorized_voter_pubkey, AccountSharedData::default()),
2600 ],
2601 instruction_accounts.clone(),
2602 Err(InstructionError::InvalidInstructionData),
2603 );
2604 return;
2605 }
2606
2607 instruction_accounts[0].is_signer = false;
2609 process_instruction_with_cu_check(
2610 features,
2611 &instruction_data,
2612 transaction_accounts.clone(),
2613 instruction_accounts.clone(),
2614 Err(InstructionError::MissingRequiredSignature),
2615 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
2616 );
2617 instruction_accounts[0].is_signer = true;
2618
2619 let accounts = process_instruction_with_cu_check(
2621 features,
2622 &instruction_data,
2623 transaction_accounts.clone(),
2624 instruction_accounts.clone(),
2625 Ok(()),
2626 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
2627 );
2628
2629 transaction_accounts[0] = (vote_pubkey, accounts[0].clone());
2631 process_instruction_with_cu_check(
2632 features,
2633 &instruction_data,
2634 transaction_accounts.clone(),
2635 instruction_accounts.clone(),
2636 Err(VoteError::TooSoonToReauthorize.into()),
2637 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
2638 );
2639
2640 instruction_accounts[0].is_signer = false;
2642 instruction_accounts.push(AccountMeta {
2643 pubkey: authorized_voter_pubkey,
2644 is_signer: true,
2645 is_writable: false,
2646 });
2647 let clock = Clock {
2648 epoch: 3,
2651 leader_schedule_epoch: 4,
2652 ..Clock::default()
2653 };
2654 let clock_account = create_sysvar_account(&clock);
2655 transaction_accounts[1] = (sysvar::clock::id(), clock_account);
2656 process_instruction_with_cu_check(
2657 features,
2658 &instruction_data,
2659 transaction_accounts.clone(),
2660 instruction_accounts.clone(),
2661 Ok(()),
2662 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
2663 );
2664 instruction_accounts[0].is_signer = true;
2665 instruction_accounts.pop();
2666
2667 let (vote, instruction_datas) = create_serialized_votes();
2669 let slot_hashes = SlotHashes::new(&[(*vote.slots.last().unwrap(), vote.hash)]);
2670 let slot_hashes_account = create_sysvar_account(&slot_hashes);
2671 transaction_accounts.push((sysvar::slot_hashes::id(), slot_hashes_account));
2672 instruction_accounts.insert(
2673 1,
2674 AccountMeta {
2675 pubkey: sysvar::slot_hashes::id(),
2676 is_signer: false,
2677 is_writable: false,
2678 },
2679 );
2680 let mut authorized_instruction_accounts = instruction_accounts.clone();
2681 authorized_instruction_accounts.push(AccountMeta {
2682 pubkey: authorized_voter_pubkey,
2683 is_signer: true,
2684 is_writable: false,
2685 });
2686
2687 for (instruction_data, is_tower_sync) in instruction_datas {
2688 process_instruction(
2689 features,
2690 &instruction_data,
2691 transaction_accounts.clone(),
2692 instruction_accounts.clone(),
2693 Err(if is_tower_sync {
2694 InstructionError::MissingRequiredSignature
2695 } else {
2696 InstructionError::InvalidInstructionData
2697 }),
2698 );
2699
2700 process_instruction(
2702 features,
2703 &instruction_data,
2704 transaction_accounts.clone(),
2705 authorized_instruction_accounts.clone(),
2706 if is_tower_sync {
2707 Ok(())
2708 } else {
2709 Err(InstructionError::InvalidInstructionData)
2710 },
2711 );
2712 }
2713 }
2714
2715 #[test]
2716 fn test_authorize_voter_with_bls_bad_proof_of_possession() {
2717 let (vote_pubkey, vote_account) = create_test_account();
2718 let authorized_voter_pubkey = solana_pubkey::new_rand();
2719 let clock = Clock {
2720 epoch: 1,
2721 leader_schedule_epoch: 2,
2722 ..Clock::default()
2723 };
2724 let clock_account = create_sysvar_account(&clock);
2725 let transaction_accounts = vec![
2726 (vote_pubkey, vote_account),
2727 (sysvar::clock::id(), clock_account),
2728 (authorized_voter_pubkey, AccountSharedData::default()),
2729 ];
2730 let instruction_accounts = vec![
2731 AccountMeta {
2732 pubkey: vote_pubkey,
2733 is_signer: true,
2734 is_writable: true,
2735 },
2736 AccountMeta {
2737 pubkey: sysvar::clock::id(),
2738 is_signer: false,
2739 is_writable: false,
2740 },
2741 ];
2742
2743 let instruction_data = serialize(&VoteInstruction::Authorize(
2745 authorized_voter_pubkey,
2746 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
2747 bls_pubkey: [1u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE],
2748 bls_proof_of_possession: [2u8; BLS_PROOF_OF_POSSESSION_COMPRESSED_SIZE],
2749 }),
2750 ))
2751 .unwrap();
2752 process_instruction_with_cu_check(
2753 VoteProgramFeatures {
2754 bls_pubkey_management_in_vote_account: true,
2755 ..Default::default()
2756 },
2757 &instruction_data,
2758 transaction_accounts,
2759 instruction_accounts,
2760 Err(InstructionError::InvalidArgument),
2761 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
2762 );
2763 }
2764
2765 #[test]
2775 fn test_bls_pubkey_rotation() {
2776 let features = VoteProgramFeatures {
2777 bls_pubkey_management_in_vote_account: true,
2778 ..Default::default()
2779 };
2780
2781 let (vote_pubkey, vote_account) = create_test_account_no_bls_key();
2784 let authorized_voter = vote_pubkey;
2785
2786 let (bls_1, pop_1) = create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
2788 let (bls_2, pop_2) = create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
2789
2790 let ix_accounts = vec![
2791 AccountMeta {
2792 pubkey: vote_pubkey,
2793 is_signer: true,
2794 is_writable: true,
2795 },
2796 AccountMeta {
2797 pubkey: sysvar::clock::id(),
2798 is_signer: false,
2799 is_writable: false,
2800 },
2801 ];
2802
2803 let clock_epoch_1 = create_sysvar_account(&Clock {
2808 epoch: 1,
2809 leader_schedule_epoch: 2,
2810 ..Clock::default()
2811 });
2812 let accounts = process_instruction_with_cu_check(
2813 features,
2814 &serialize(&VoteInstruction::Authorize(
2815 authorized_voter,
2816 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
2817 bls_pubkey: bls_1,
2818 bls_proof_of_possession: pop_1,
2819 }),
2820 ))
2821 .unwrap(),
2822 vec![
2823 (vote_pubkey, vote_account),
2824 (sysvar::clock::id(), clock_epoch_1.clone()),
2825 ],
2826 ix_accounts.clone(),
2827 Ok(()),
2828 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
2829 );
2830 let v4 = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
2831 assert_eq!(v4.as_ref_v4().bls_pubkey_compressed, Some(bls_1));
2832
2833 let accounts = process_instruction_with_cu_check(
2840 features,
2841 &serialize(&VoteInstruction::Authorize(
2842 authorized_voter,
2843 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
2844 bls_pubkey: bls_2,
2845 bls_proof_of_possession: pop_2,
2846 }),
2847 ))
2848 .unwrap(),
2849 vec![
2850 (vote_pubkey, accounts[0].clone()),
2851 (sysvar::clock::id(), clock_epoch_1),
2852 ],
2853 ix_accounts.clone(),
2854 Err(VoteError::TooSoonToReauthorize.into()),
2855 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
2856 );
2857 let v4 = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
2858 assert_eq!(v4.as_ref_v4().bls_pubkey_compressed, Some(bls_1)); let clock_epoch_2 = create_sysvar_account(&Clock {
2866 epoch: 2,
2867 leader_schedule_epoch: 3,
2868 ..Clock::default()
2869 });
2870 let accounts = process_instruction_with_cu_check(
2871 features,
2872 &serialize(&VoteInstruction::Authorize(
2873 authorized_voter,
2874 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
2875 bls_pubkey: bls_2,
2876 bls_proof_of_possession: pop_2,
2877 }),
2878 ))
2879 .unwrap(),
2880 vec![
2881 (vote_pubkey, accounts[0].clone()),
2882 (sysvar::clock::id(), clock_epoch_2),
2883 ],
2884 ix_accounts.clone(),
2885 Ok(()),
2886 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
2887 );
2888 let v4 = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
2889 assert_eq!(v4.as_ref_v4().bls_pubkey_compressed, Some(bls_2)); }
2891
2892 #[test]
2893 fn test_authorize_withdrawer() {
2894 let (vote_pubkey, vote_account) = create_test_account();
2895 let authorized_withdrawer_pubkey = solana_pubkey::new_rand();
2896 let instruction_data = serialize(&VoteInstruction::Authorize(
2897 authorized_withdrawer_pubkey,
2898 VoteAuthorize::Withdrawer,
2899 ))
2900 .unwrap();
2901 let mut transaction_accounts = vec![
2902 (vote_pubkey, vote_account),
2903 (sysvar::clock::id(), create_default_clock_account()),
2904 (authorized_withdrawer_pubkey, AccountSharedData::default()),
2905 ];
2906 let mut instruction_accounts = vec![
2907 AccountMeta {
2908 pubkey: vote_pubkey,
2909 is_signer: true,
2910 is_writable: true,
2911 },
2912 AccountMeta {
2913 pubkey: sysvar::clock::id(),
2914 is_signer: false,
2915 is_writable: false,
2916 },
2917 ];
2918
2919 let features = VoteProgramFeatures {
2920 ..Default::default()
2921 };
2922
2923 instruction_accounts[0].is_signer = false;
2925 process_instruction(
2926 features,
2927 &instruction_data,
2928 transaction_accounts.clone(),
2929 instruction_accounts.clone(),
2930 Err(InstructionError::MissingRequiredSignature),
2931 );
2932 instruction_accounts[0].is_signer = true;
2933
2934 let accounts = process_instruction(
2936 features,
2937 &instruction_data,
2938 transaction_accounts.clone(),
2939 instruction_accounts.clone(),
2940 Ok(()),
2941 );
2942
2943 instruction_accounts[0].is_signer = false;
2945 instruction_accounts.push(AccountMeta {
2946 pubkey: authorized_withdrawer_pubkey,
2947 is_signer: true,
2948 is_writable: false,
2949 });
2950 transaction_accounts[0] = (vote_pubkey, accounts[0].clone());
2951 process_instruction(
2952 features,
2953 &instruction_data,
2954 transaction_accounts.clone(),
2955 instruction_accounts.clone(),
2956 Ok(()),
2957 );
2958
2959 let authorized_voter_pubkey = solana_pubkey::new_rand();
2961 transaction_accounts.push((authorized_voter_pubkey, AccountSharedData::default()));
2962 let instruction_data = serialize(&VoteInstruction::Authorize(
2963 authorized_voter_pubkey,
2964 VoteAuthorize::Voter,
2965 ))
2966 .unwrap();
2967 process_instruction(
2968 features,
2969 &instruction_data,
2970 transaction_accounts.clone(),
2971 instruction_accounts.clone(),
2972 Ok(()),
2973 );
2974 }
2975
2976 #[test]
2977 fn test_vote_withdraw() {
2978 let (vote_pubkey, vote_account) = create_test_account();
2979 let lamports = vote_account.lamports();
2980 let authorized_withdrawer_pubkey = solana_pubkey::new_rand();
2981 let mut transaction_accounts = vec![
2982 (vote_pubkey, vote_account.clone()),
2983 (sysvar::clock::id(), create_default_clock_account()),
2984 (sysvar::rent::id(), create_default_rent_account()),
2985 (authorized_withdrawer_pubkey, AccountSharedData::default()),
2986 ];
2987 let mut instruction_accounts = vec![
2988 AccountMeta {
2989 pubkey: vote_pubkey,
2990 is_signer: true,
2991 is_writable: true,
2992 },
2993 AccountMeta {
2994 pubkey: sysvar::clock::id(),
2995 is_signer: false,
2996 is_writable: false,
2997 },
2998 ];
2999
3000 let features = VoteProgramFeatures {
3001 ..Default::default()
3002 };
3003
3004 let accounts = process_instruction(
3006 features,
3007 &serialize(&VoteInstruction::Authorize(
3008 authorized_withdrawer_pubkey,
3009 VoteAuthorize::Withdrawer,
3010 ))
3011 .unwrap(),
3012 transaction_accounts.clone(),
3013 instruction_accounts.clone(),
3014 Ok(()),
3015 );
3016 instruction_accounts[0].is_signer = false;
3017 instruction_accounts[1] = AccountMeta {
3018 pubkey: authorized_withdrawer_pubkey,
3019 is_signer: true,
3020 is_writable: true,
3021 };
3022 transaction_accounts[0] = (vote_pubkey, accounts[0].clone());
3023 let accounts = process_instruction(
3024 features,
3025 &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
3026 transaction_accounts.clone(),
3027 instruction_accounts.clone(),
3028 Ok(()),
3029 );
3030 assert_eq!(accounts[0].lamports(), 0);
3031 assert_eq!(accounts[3].lamports(), lamports);
3032 let post_state: VoteStateVersions = accounts[0].state().unwrap();
3033 assert!(post_state.is_uninitialized());
3035
3036 transaction_accounts[0] = (vote_pubkey, vote_account);
3038 process_instruction(
3039 features,
3040 &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
3041 transaction_accounts.clone(),
3042 instruction_accounts.clone(),
3043 Err(InstructionError::MissingRequiredSignature),
3044 );
3045 instruction_accounts[0].is_signer = true;
3046
3047 process_instruction(
3049 features,
3050 &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
3051 transaction_accounts.clone(),
3052 instruction_accounts.clone(),
3053 Ok(()),
3054 );
3055
3056 process_instruction(
3058 features,
3059 &serialize(&VoteInstruction::Withdraw(lamports + 1)).unwrap(),
3060 transaction_accounts.clone(),
3061 instruction_accounts.clone(),
3062 Err(InstructionError::InsufficientFunds),
3063 );
3064
3065 let withdraw_lamports = 42;
3067 let accounts = process_instruction(
3068 features,
3069 &serialize(&VoteInstruction::Withdraw(withdraw_lamports)).unwrap(),
3070 transaction_accounts,
3071 instruction_accounts,
3072 Ok(()),
3073 );
3074 assert_eq!(accounts[0].lamports(), lamports - withdraw_lamports);
3075 assert_eq!(accounts[3].lamports(), withdraw_lamports);
3076 }
3077
3078 #[test]
3079 fn test_vote_state_withdraw() {
3080 let authorized_withdrawer_pubkey = solana_pubkey::new_rand();
3081 let (vote_pubkey_1, vote_account_with_epoch_credits_1) =
3082 create_test_account_with_epoch_credits(&[2, 1]);
3083 let (vote_pubkey_2, vote_account_with_epoch_credits_2) =
3084 create_test_account_with_epoch_credits(&[2, 1, 3]);
3085 let clock = Clock {
3086 epoch: 3,
3087 ..Clock::default()
3088 };
3089 let clock_account = create_sysvar_account(&clock);
3090 let rent_sysvar = Rent::default();
3091 let minimum_balance = rent_sysvar
3092 .minimum_balance(vote_account_with_epoch_credits_1.data().len())
3093 .max(1);
3094 let lamports = vote_account_with_epoch_credits_1.lamports();
3095 let transaction_accounts = vec![
3096 (vote_pubkey_1, vote_account_with_epoch_credits_1),
3097 (vote_pubkey_2, vote_account_with_epoch_credits_2),
3098 (sysvar::clock::id(), clock_account),
3099 (sysvar::rent::id(), create_sysvar_account(&rent_sysvar)),
3100 (authorized_withdrawer_pubkey, AccountSharedData::default()),
3101 ];
3102 let mut instruction_accounts = vec![
3103 AccountMeta {
3104 pubkey: vote_pubkey_1,
3105 is_signer: true,
3106 is_writable: true,
3107 },
3108 AccountMeta {
3109 pubkey: authorized_withdrawer_pubkey,
3110 is_signer: false,
3111 is_writable: true,
3112 },
3113 ];
3114
3115 let features = VoteProgramFeatures {
3116 ..Default::default()
3117 };
3118
3119 instruction_accounts[0].pubkey = vote_pubkey_1;
3121 process_instruction(
3122 features,
3123 &serialize(&VoteInstruction::Withdraw(lamports - minimum_balance + 1)).unwrap(),
3124 transaction_accounts.clone(),
3125 instruction_accounts.clone(),
3126 Err(InstructionError::InsufficientFunds),
3127 );
3128
3129 instruction_accounts[0].pubkey = vote_pubkey_2;
3131 process_instruction(
3132 features,
3133 &serialize(&VoteInstruction::Withdraw(lamports - minimum_balance + 1)).unwrap(),
3134 transaction_accounts.clone(),
3135 instruction_accounts.clone(),
3136 Err(InstructionError::InsufficientFunds),
3137 );
3138
3139 instruction_accounts[0].pubkey = vote_pubkey_1;
3141 process_instruction(
3142 features,
3143 &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
3144 transaction_accounts.clone(),
3145 instruction_accounts.clone(),
3146 Ok(()),
3147 );
3148
3149 instruction_accounts[0].pubkey = vote_pubkey_2;
3151 process_instruction(
3152 features,
3153 &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
3154 transaction_accounts,
3155 instruction_accounts,
3156 Err(VoteError::ActiveVoteAccountClose.into()),
3157 );
3158 }
3159
3160 #[test]
3161 fn test_deinitialized_account_full_lifecycle_v4() {
3162 let (vote_pubkey, _authorized_voter, authorized_withdrawer, vote_account) =
3166 create_test_account_with_authorized();
3167 let lamports = vote_account.lamports();
3168
3169 let features = VoteProgramFeatures {
3170 ..Default::default()
3171 };
3172
3173 let recipient_pubkey = solana_pubkey::new_rand();
3174 let transaction_accounts = vec![
3175 (vote_pubkey, vote_account),
3176 (recipient_pubkey, AccountSharedData::default()),
3177 (authorized_withdrawer, AccountSharedData::default()),
3178 (sysvar::rent::id(), create_default_rent_account()),
3179 (sysvar::clock::id(), create_default_clock_account()),
3180 ];
3181 let instruction_accounts = vec![
3182 AccountMeta {
3183 pubkey: vote_pubkey,
3184 is_signer: false,
3185 is_writable: true,
3186 },
3187 AccountMeta {
3188 pubkey: recipient_pubkey,
3189 is_signer: false,
3190 is_writable: true,
3191 },
3192 AccountMeta {
3193 pubkey: authorized_withdrawer,
3194 is_signer: true,
3195 is_writable: false,
3196 },
3197 ];
3198
3199 let accounts = process_instruction(
3201 features,
3202 &serialize(&VoteInstruction::Withdraw(lamports)).unwrap(),
3203 transaction_accounts,
3204 instruction_accounts,
3205 Ok(()),
3206 );
3207 let deinitialized_vote_account = &accounts[0];
3208
3209 assert!(deinitialized_vote_account.data().iter().all(|&b| b == 0));
3211
3212 let clock_account = create_sysvar_account(&Clock {
3214 epoch: 100,
3215 ..Clock::default()
3216 });
3217 process_instruction(
3218 features,
3219 &serialize(&VoteInstruction::Authorize(
3220 solana_pubkey::new_rand(),
3221 VoteAuthorize::Voter,
3222 ))
3223 .unwrap(),
3224 vec![
3225 (vote_pubkey, deinitialized_vote_account.clone()),
3226 (sysvar::clock::id(), clock_account),
3227 (authorized_withdrawer, AccountSharedData::default()),
3228 ],
3229 vec![
3230 AccountMeta {
3231 pubkey: vote_pubkey,
3232 is_signer: true,
3233 is_writable: true,
3234 },
3235 AccountMeta {
3236 pubkey: sysvar::clock::id(),
3237 is_signer: false,
3238 is_writable: false,
3239 },
3240 ],
3241 Err(InstructionError::InvalidAccountData),
3242 );
3243
3244 let new_node_pubkey = solana_pubkey::new_rand();
3246 let new_vote_init = VoteInit {
3247 node_pubkey: new_node_pubkey,
3248 authorized_voter: solana_pubkey::new_rand(),
3249 authorized_withdrawer: solana_pubkey::new_rand(),
3250 commission: 10,
3251 };
3252 let mut funded_account = deinitialized_vote_account.clone();
3254 let rent = Rent::default();
3255 funded_account.set_lamports(rent.minimum_balance(funded_account.data().len()));
3256
3257 let accounts = process_instruction(
3258 features,
3259 &serialize(&VoteInstruction::InitializeAccount(new_vote_init)).unwrap(),
3260 vec![
3261 (vote_pubkey, funded_account),
3262 (sysvar::rent::id(), create_default_rent_account()),
3263 (sysvar::clock::id(), create_default_clock_account()),
3264 (new_node_pubkey, AccountSharedData::default()),
3265 ],
3266 vec![
3267 AccountMeta {
3268 pubkey: vote_pubkey,
3269 is_signer: false,
3270 is_writable: true,
3271 },
3272 AccountMeta {
3273 pubkey: sysvar::rent::id(),
3274 is_signer: false,
3275 is_writable: false,
3276 },
3277 AccountMeta {
3278 pubkey: sysvar::clock::id(),
3279 is_signer: false,
3280 is_writable: false,
3281 },
3282 AccountMeta {
3283 pubkey: new_node_pubkey,
3284 is_signer: true,
3285 is_writable: false,
3286 },
3287 ],
3288 Ok(()),
3289 );
3290
3291 let vote_state = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
3293 assert_eq!(*vote_state.node_pubkey(), new_node_pubkey);
3294 assert_eq!(
3295 *vote_state.authorized_withdrawer(),
3296 new_vote_init.authorized_withdrawer
3297 );
3298 assert_eq!(vote_state.commission(), 10);
3299 assert!(vote_state.votes().is_empty());
3300 assert!(vote_state.epoch_credits().is_empty());
3301 }
3302
3303 #[test]
3304 fn test_uninitialized_v3_blocked_under_v4() {
3305 let vote_pubkey = solana_pubkey::new_rand();
3308
3309 let uninitialized_v3 = VoteStateVersions::V3(Box::default());
3311 let serialized = bincode::serialize(&uninitialized_v3).unwrap();
3312 let target_len = vote_state_size_of();
3313 let mut data = vec![0u8; target_len];
3314 data[..serialized.len()].copy_from_slice(&serialized);
3315
3316 let rent = Rent::default();
3317 let lamports = rent.minimum_balance(target_len);
3318 let mut vote_account = AccountSharedData::new(lamports, target_len, &id());
3319 vote_account.set_data_from_slice(&data);
3320
3321 let authorized_withdrawer = solana_pubkey::new_rand();
3322 let features = VoteProgramFeatures::all_enabled();
3323
3324 process_instruction(
3326 features,
3327 &serialize(&VoteInstruction::Authorize(
3328 solana_pubkey::new_rand(),
3329 VoteAuthorize::Voter,
3330 ))
3331 .unwrap(),
3332 vec![
3333 (vote_pubkey, vote_account.clone()),
3334 (sysvar::clock::id(), create_default_clock_account()),
3335 (authorized_withdrawer, AccountSharedData::default()),
3336 ],
3337 vec![
3338 AccountMeta {
3339 pubkey: vote_pubkey,
3340 is_signer: false,
3341 is_writable: true,
3342 },
3343 AccountMeta {
3344 pubkey: sysvar::clock::id(),
3345 is_signer: false,
3346 is_writable: false,
3347 },
3348 AccountMeta {
3349 pubkey: authorized_withdrawer,
3350 is_signer: true,
3351 is_writable: false,
3352 },
3353 ],
3354 Err(InstructionError::UninitializedAccount),
3355 );
3356
3357 process_instruction(
3359 features,
3360 &serialize(&VoteInstruction::UpdateCommission(50)).unwrap(),
3361 vec![
3362 (vote_pubkey, vote_account.clone()),
3363 (authorized_withdrawer, AccountSharedData::default()),
3364 (sysvar::clock::id(), create_default_clock_account()),
3365 (
3366 sysvar::epoch_schedule::id(),
3367 create_sysvar_account(&solana_epoch_schedule::EpochSchedule::without_warmup()),
3368 ),
3369 ],
3370 vec![
3371 AccountMeta {
3372 pubkey: vote_pubkey,
3373 is_signer: false,
3374 is_writable: true,
3375 },
3376 AccountMeta {
3377 pubkey: authorized_withdrawer,
3378 is_signer: true,
3379 is_writable: false,
3380 },
3381 ],
3382 Err(InstructionError::UninitializedAccount),
3383 );
3384
3385 let new_node = solana_pubkey::new_rand();
3387 let vote_init = VoteInit {
3388 node_pubkey: new_node,
3389 authorized_voter: solana_pubkey::new_rand(),
3390 authorized_withdrawer: solana_pubkey::new_rand(),
3391 commission: 5,
3392 };
3393 let accounts = process_instruction(
3394 features,
3395 &serialize(&VoteInstruction::InitializeAccount(vote_init)).unwrap(),
3396 vec![
3397 (vote_pubkey, vote_account.clone()),
3398 (sysvar::rent::id(), create_default_rent_account()),
3399 (sysvar::clock::id(), create_default_clock_account()),
3400 (new_node, AccountSharedData::default()),
3401 ],
3402 vec![
3403 AccountMeta {
3404 pubkey: vote_pubkey,
3405 is_signer: false,
3406 is_writable: true,
3407 },
3408 AccountMeta {
3409 pubkey: sysvar::rent::id(),
3410 is_signer: false,
3411 is_writable: false,
3412 },
3413 AccountMeta {
3414 pubkey: sysvar::clock::id(),
3415 is_signer: false,
3416 is_writable: false,
3417 },
3418 AccountMeta {
3419 pubkey: new_node,
3420 is_signer: true,
3421 is_writable: false,
3422 },
3423 ],
3424 Ok(()),
3425 );
3426
3427 let versioned: VoteStateVersions = accounts[0].state().unwrap();
3429 assert!(matches!(versioned, VoteStateVersions::V4(_)));
3430 let vote_state = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
3431 assert_eq!(*vote_state.node_pubkey(), new_node);
3432 assert_eq!(vote_state.commission(), 5);
3433
3434 let new_node = solana_pubkey::new_rand();
3436 let (bls_pubkey, bls_proof_of_possession) =
3437 create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
3438 let inflation_rewards_collector = solana_pubkey::new_rand();
3439 let block_revenue_collector = solana_pubkey::new_rand();
3440 let vote_init_v2 = VoteInitV2 {
3441 node_pubkey: new_node,
3442 authorized_voter: solana_pubkey::new_rand(),
3443 authorized_voter_bls_pubkey: bls_pubkey,
3444 authorized_voter_bls_proof_of_possession: bls_proof_of_possession,
3445 authorized_withdrawer: solana_pubkey::new_rand(),
3446 inflation_rewards_commission_bps: 1_234,
3447 block_revenue_commission_bps: 5_678,
3448 };
3449
3450 let collector_account = AccountSharedData::new(
3451 rent.minimum_balance(0),
3452 0,
3453 &solana_sdk_ids::system_program::id(),
3454 );
3455
3456 let accounts = process_instruction_with_cu_check(
3457 features,
3458 &serialize(&VoteInstruction::InitializeAccountV2(vote_init_v2)).unwrap(),
3459 vec![
3460 (vote_pubkey, vote_account),
3461 (new_node, AccountSharedData::default()),
3462 (inflation_rewards_collector, collector_account.clone()),
3463 (block_revenue_collector, collector_account),
3464 (sysvar::rent::id(), create_default_rent_account()),
3465 (sysvar::clock::id(), create_default_clock_account()),
3466 ],
3467 vec![
3468 AccountMeta {
3469 pubkey: vote_pubkey,
3470 is_signer: false,
3471 is_writable: true,
3472 },
3473 AccountMeta {
3474 pubkey: new_node,
3475 is_signer: true,
3476 is_writable: false,
3477 },
3478 AccountMeta {
3479 pubkey: inflation_rewards_collector,
3480 is_signer: false,
3481 is_writable: true,
3482 },
3483 AccountMeta {
3484 pubkey: block_revenue_collector,
3485 is_signer: false,
3486 is_writable: true,
3487 },
3488 ],
3489 Ok(()),
3490 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
3491 );
3492
3493 let versioned: VoteStateVersions = accounts[0].state().unwrap();
3495 assert!(matches!(versioned, VoteStateVersions::V4(_)));
3496 let vote_state = deserialize_vote_state_for_test(accounts[0].data(), &vote_pubkey);
3497 let v4 = vote_state.as_ref_v4();
3498 assert_eq!(v4.node_pubkey, new_node);
3499 assert_eq!(v4.bls_pubkey_compressed, Some(bls_pubkey));
3500 assert_eq!(v4.inflation_rewards_commission_bps, 1_234);
3501 assert_eq!(v4.block_revenue_commission_bps, 5_678);
3502 assert_eq!(v4.inflation_rewards_collector, inflation_rewards_collector);
3503 assert_eq!(v4.block_revenue_collector, block_revenue_collector);
3504 }
3505
3506 fn perform_authorize_with_seed_test(
3507 bls_pubkey_management_in_vote_account: bool,
3508 authorization_type: VoteAuthorize,
3509 vote_pubkey: Pubkey,
3510 vote_account: AccountSharedData,
3511 current_authority_base_key: Pubkey,
3512 current_authority_seed: String,
3513 current_authority_owner: Pubkey,
3514 new_authority_pubkey: Pubkey,
3515 ) {
3516 let clock = Clock {
3517 epoch: 1,
3518 leader_schedule_epoch: 2,
3519 ..Clock::default()
3520 };
3521 let clock_account = create_sysvar_account(&clock);
3522 let transaction_accounts = vec![
3523 (vote_pubkey, vote_account),
3524 (sysvar::clock::id(), clock_account),
3525 (current_authority_base_key, AccountSharedData::default()),
3526 ];
3527 let mut instruction_accounts = vec![
3528 AccountMeta {
3529 pubkey: vote_pubkey,
3530 is_signer: false,
3531 is_writable: true,
3532 },
3533 AccountMeta {
3534 pubkey: sysvar::clock::id(),
3535 is_signer: false,
3536 is_writable: false,
3537 },
3538 AccountMeta {
3539 pubkey: current_authority_base_key,
3540 is_signer: true,
3541 is_writable: false,
3542 },
3543 ];
3544
3545 let features = VoteProgramFeatures {
3546 bls_pubkey_management_in_vote_account,
3547 ..Default::default()
3548 };
3549 let expected_cus = if matches!(authorization_type, VoteAuthorize::VoterWithBLS(_)) {
3550 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS
3551 } else {
3552 DEFAULT_COMPUTE_UNITS
3553 };
3554
3555 instruction_accounts[2].is_signer = false;
3557 process_instruction_with_cu_check(
3558 features,
3559 &serialize(&VoteInstruction::AuthorizeWithSeed(
3560 VoteAuthorizeWithSeedArgs {
3561 authorization_type,
3562 current_authority_derived_key_owner: current_authority_owner,
3563 current_authority_derived_key_seed: current_authority_seed.clone(),
3564 new_authority: new_authority_pubkey,
3565 },
3566 ))
3567 .unwrap(),
3568 transaction_accounts.clone(),
3569 instruction_accounts.clone(),
3570 Err(InstructionError::MissingRequiredSignature),
3571 expected_cus,
3572 );
3573 instruction_accounts[2].is_signer = true;
3574
3575 process_instruction_with_cu_check(
3577 features,
3578 &serialize(&VoteInstruction::AuthorizeWithSeed(
3579 VoteAuthorizeWithSeedArgs {
3580 authorization_type,
3581 current_authority_derived_key_owner: current_authority_owner,
3582 current_authority_derived_key_seed: String::from("WRONG_SEED"),
3583 new_authority: new_authority_pubkey,
3584 },
3585 ))
3586 .unwrap(),
3587 transaction_accounts.clone(),
3588 instruction_accounts.clone(),
3589 Err(InstructionError::MissingRequiredSignature),
3590 expected_cus,
3591 );
3592
3593 process_instruction_with_cu_check(
3595 features,
3596 &serialize(&VoteInstruction::AuthorizeWithSeed(
3597 VoteAuthorizeWithSeedArgs {
3598 authorization_type,
3599 current_authority_derived_key_owner: Pubkey::new_unique(), current_authority_derived_key_seed: current_authority_seed.clone(),
3601 new_authority: new_authority_pubkey,
3602 },
3603 ))
3604 .unwrap(),
3605 transaction_accounts.clone(),
3606 instruction_accounts.clone(),
3607 Err(InstructionError::MissingRequiredSignature),
3608 expected_cus,
3609 );
3610
3611 process_instruction_with_cu_check(
3613 features,
3614 &serialize(&VoteInstruction::AuthorizeWithSeed(
3615 VoteAuthorizeWithSeedArgs {
3616 authorization_type,
3617 current_authority_derived_key_owner: current_authority_owner,
3618 current_authority_derived_key_seed: current_authority_seed,
3619 new_authority: new_authority_pubkey,
3620 },
3621 ))
3622 .unwrap(),
3623 transaction_accounts,
3624 instruction_accounts,
3625 Ok(()),
3626 expected_cus,
3627 );
3628 }
3629
3630 fn perform_authorize_checked_with_seed_test(
3631 bls_pubkey_management_in_vote_account: bool,
3632 authorization_type: VoteAuthorize,
3633 vote_pubkey: Pubkey,
3634 vote_account: AccountSharedData,
3635 current_authority_base_key: Pubkey,
3636 current_authority_seed: String,
3637 current_authority_owner: Pubkey,
3638 new_authority_pubkey: Pubkey,
3639 ) {
3640 let clock = Clock {
3641 epoch: 1,
3642 leader_schedule_epoch: 2,
3643 ..Clock::default()
3644 };
3645 let clock_account = create_sysvar_account(&clock);
3646 let transaction_accounts = vec![
3647 (vote_pubkey, vote_account),
3648 (sysvar::clock::id(), clock_account),
3649 (current_authority_base_key, AccountSharedData::default()),
3650 (new_authority_pubkey, AccountSharedData::default()),
3651 ];
3652 let mut instruction_accounts = vec![
3653 AccountMeta {
3654 pubkey: vote_pubkey,
3655 is_signer: false,
3656 is_writable: true,
3657 },
3658 AccountMeta {
3659 pubkey: sysvar::clock::id(),
3660 is_signer: false,
3661 is_writable: false,
3662 },
3663 AccountMeta {
3664 pubkey: current_authority_base_key,
3665 is_signer: true,
3666 is_writable: false,
3667 },
3668 AccountMeta {
3669 pubkey: new_authority_pubkey,
3670 is_signer: true,
3671 is_writable: false,
3672 },
3673 ];
3674
3675 let features = VoteProgramFeatures {
3676 bls_pubkey_management_in_vote_account,
3677 ..Default::default()
3678 };
3679 let expected_cus = if matches!(authorization_type, VoteAuthorize::VoterWithBLS(_)) {
3680 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS
3681 } else {
3682 DEFAULT_COMPUTE_UNITS
3683 };
3684
3685 instruction_accounts[2].is_signer = false;
3687 process_instruction_with_cu_check(
3688 features,
3689 &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
3690 VoteAuthorizeCheckedWithSeedArgs {
3691 authorization_type,
3692 current_authority_derived_key_owner: current_authority_owner,
3693 current_authority_derived_key_seed: current_authority_seed.clone(),
3694 },
3695 ))
3696 .unwrap(),
3697 transaction_accounts.clone(),
3698 instruction_accounts.clone(),
3699 Err(InstructionError::MissingRequiredSignature),
3700 expected_cus,
3701 );
3702 instruction_accounts[2].is_signer = true;
3703
3704 instruction_accounts[3].is_signer = false;
3707 process_instruction(
3708 features,
3709 &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
3710 VoteAuthorizeCheckedWithSeedArgs {
3711 authorization_type,
3712 current_authority_derived_key_owner: current_authority_owner,
3713 current_authority_derived_key_seed: current_authority_seed.clone(),
3714 },
3715 ))
3716 .unwrap(),
3717 transaction_accounts.clone(),
3718 instruction_accounts.clone(),
3719 Err(InstructionError::MissingRequiredSignature),
3720 );
3721 instruction_accounts[3].is_signer = true;
3722
3723 process_instruction_with_cu_check(
3725 features,
3726 &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
3727 VoteAuthorizeCheckedWithSeedArgs {
3728 authorization_type,
3729 current_authority_derived_key_owner: current_authority_owner,
3730 current_authority_derived_key_seed: String::from("WRONG_SEED"),
3731 },
3732 ))
3733 .unwrap(),
3734 transaction_accounts.clone(),
3735 instruction_accounts.clone(),
3736 Err(InstructionError::MissingRequiredSignature),
3737 expected_cus,
3738 );
3739
3740 process_instruction_with_cu_check(
3742 features,
3743 &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
3744 VoteAuthorizeCheckedWithSeedArgs {
3745 authorization_type,
3746 current_authority_derived_key_owner: Pubkey::new_unique(), current_authority_derived_key_seed: current_authority_seed.clone(),
3748 },
3749 ))
3750 .unwrap(),
3751 transaction_accounts.clone(),
3752 instruction_accounts.clone(),
3753 Err(InstructionError::MissingRequiredSignature),
3754 expected_cus,
3755 );
3756
3757 process_instruction_with_cu_check(
3759 features,
3760 &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
3761 VoteAuthorizeCheckedWithSeedArgs {
3762 authorization_type,
3763 current_authority_derived_key_owner: current_authority_owner,
3764 current_authority_derived_key_seed: current_authority_seed,
3765 },
3766 ))
3767 .unwrap(),
3768 transaction_accounts,
3769 instruction_accounts,
3770 Ok(()),
3771 expected_cus,
3772 );
3773 }
3774
3775 #[test_matrix([false, true])]
3776 fn test_voter_base_key_can_authorize_new_voter(bls_pubkey_management_in_vote_account: bool) {
3777 let VoteAccountTestFixtureWithAuthorities {
3778 vote_pubkey,
3779 voter_base_key,
3780 voter_owner,
3781 voter_seed,
3782 vote_account,
3783 ..
3784 } = create_test_account_with_authorized_from_seed();
3785 let new_voter_pubkey = Pubkey::new_unique();
3786 let (bls_pubkey, bls_proof_of_possession) =
3787 create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
3788 let authorize_type = if bls_pubkey_management_in_vote_account {
3789 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
3790 bls_pubkey,
3791 bls_proof_of_possession,
3792 })
3793 } else {
3794 VoteAuthorize::Voter
3795 };
3796 perform_authorize_with_seed_test(
3797 bls_pubkey_management_in_vote_account,
3798 authorize_type,
3799 vote_pubkey,
3800 vote_account,
3801 voter_base_key,
3802 voter_seed,
3803 voter_owner,
3804 new_voter_pubkey,
3805 );
3806 }
3807
3808 #[test_matrix([false, true])]
3809 fn test_withdrawer_base_key_can_authorize_new_voter(
3810 bls_pubkey_management_in_vote_account: bool,
3811 ) {
3812 let VoteAccountTestFixtureWithAuthorities {
3813 vote_pubkey,
3814 withdrawer_base_key,
3815 withdrawer_owner,
3816 withdrawer_seed,
3817 vote_account,
3818 ..
3819 } = create_test_account_with_authorized_from_seed();
3820 let new_voter_pubkey = Pubkey::new_unique();
3821 let (bls_pubkey, bls_proof_of_possession) =
3822 create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
3823 let authorize_type = if bls_pubkey_management_in_vote_account {
3824 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
3825 bls_pubkey,
3826 bls_proof_of_possession,
3827 })
3828 } else {
3829 VoteAuthorize::Voter
3830 };
3831 perform_authorize_with_seed_test(
3832 bls_pubkey_management_in_vote_account,
3833 authorize_type,
3834 vote_pubkey,
3835 vote_account,
3836 withdrawer_base_key,
3837 withdrawer_seed,
3838 withdrawer_owner,
3839 new_voter_pubkey,
3840 );
3841 }
3842
3843 #[test]
3844 fn test_voter_base_key_can_not_authorize_new_withdrawer() {
3845 let VoteAccountTestFixtureWithAuthorities {
3846 vote_pubkey,
3847 voter_base_key,
3848 voter_owner,
3849 voter_seed,
3850 vote_account,
3851 ..
3852 } = create_test_account_with_authorized_from_seed();
3853 let new_withdrawer_pubkey = Pubkey::new_unique();
3854 let clock = Clock {
3855 epoch: 1,
3856 leader_schedule_epoch: 2,
3857 ..Clock::default()
3858 };
3859 let clock_account = create_sysvar_account(&clock);
3860 let transaction_accounts = vec![
3861 (vote_pubkey, vote_account),
3862 (sysvar::clock::id(), clock_account),
3863 (voter_base_key, AccountSharedData::default()),
3864 ];
3865 let instruction_accounts = vec![
3866 AccountMeta {
3867 pubkey: vote_pubkey,
3868 is_signer: false,
3869 is_writable: true,
3870 },
3871 AccountMeta {
3872 pubkey: sysvar::clock::id(),
3873 is_signer: false,
3874 is_writable: false,
3875 },
3876 AccountMeta {
3877 pubkey: voter_base_key,
3878 is_signer: true,
3879 is_writable: false,
3880 },
3881 ];
3882 process_instruction(
3884 VoteProgramFeatures {
3885 ..Default::default()
3886 },
3887 &serialize(&VoteInstruction::AuthorizeWithSeed(
3888 VoteAuthorizeWithSeedArgs {
3889 authorization_type: VoteAuthorize::Withdrawer,
3890 current_authority_derived_key_owner: voter_owner,
3891 current_authority_derived_key_seed: voter_seed,
3892 new_authority: new_withdrawer_pubkey,
3893 },
3894 ))
3895 .unwrap(),
3896 transaction_accounts,
3897 instruction_accounts,
3898 Err(InstructionError::MissingRequiredSignature),
3899 );
3900 }
3901
3902 #[test_matrix([false, true])]
3903 fn test_withdrawer_base_key_can_authorize_new_withdrawer(
3904 bls_pubkey_management_in_vote_account: bool,
3905 ) {
3906 let VoteAccountTestFixtureWithAuthorities {
3907 vote_pubkey,
3908 withdrawer_base_key,
3909 withdrawer_owner,
3910 withdrawer_seed,
3911 vote_account,
3912 ..
3913 } = create_test_account_with_authorized_from_seed();
3914 let new_withdrawer_pubkey = Pubkey::new_unique();
3915 perform_authorize_with_seed_test(
3916 bls_pubkey_management_in_vote_account,
3917 VoteAuthorize::Withdrawer,
3918 vote_pubkey,
3919 vote_account,
3920 withdrawer_base_key,
3921 withdrawer_seed,
3922 withdrawer_owner,
3923 new_withdrawer_pubkey,
3924 );
3925 }
3926
3927 #[test_matrix([false, true])]
3928 fn test_voter_base_key_can_authorize_new_voter_checked(
3929 bls_pubkey_management_in_vote_account: bool,
3930 ) {
3931 let VoteAccountTestFixtureWithAuthorities {
3932 vote_pubkey,
3933 voter_base_key,
3934 voter_owner,
3935 voter_seed,
3936 vote_account,
3937 ..
3938 } = create_test_account_with_authorized_from_seed();
3939 let new_voter_pubkey = Pubkey::new_unique();
3940 let (bls_pubkey, bls_proof_of_possession) =
3941 create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
3942 let authorize_type = if bls_pubkey_management_in_vote_account {
3943 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
3944 bls_pubkey,
3945 bls_proof_of_possession,
3946 })
3947 } else {
3948 VoteAuthorize::Voter
3949 };
3950 perform_authorize_checked_with_seed_test(
3951 bls_pubkey_management_in_vote_account,
3952 authorize_type,
3953 vote_pubkey,
3954 vote_account,
3955 voter_base_key,
3956 voter_seed,
3957 voter_owner,
3958 new_voter_pubkey,
3959 );
3960 }
3961
3962 #[test_matrix([false, true])]
3963 fn test_withdrawer_base_key_can_authorize_new_voter_checked(
3964 bls_pubkey_management_in_vote_account: bool,
3965 ) {
3966 let VoteAccountTestFixtureWithAuthorities {
3967 vote_pubkey,
3968 withdrawer_base_key,
3969 withdrawer_owner,
3970 withdrawer_seed,
3971 vote_account,
3972 ..
3973 } = create_test_account_with_authorized_from_seed();
3974 let new_voter_pubkey = Pubkey::new_unique();
3975 let (bls_pubkey, bls_proof_of_possession) =
3976 create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
3977 let authorize_type = if bls_pubkey_management_in_vote_account {
3978 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
3979 bls_pubkey,
3980 bls_proof_of_possession,
3981 })
3982 } else {
3983 VoteAuthorize::Voter
3984 };
3985 perform_authorize_checked_with_seed_test(
3986 bls_pubkey_management_in_vote_account,
3987 authorize_type,
3988 vote_pubkey,
3989 vote_account,
3990 withdrawer_base_key,
3991 withdrawer_seed,
3992 withdrawer_owner,
3993 new_voter_pubkey,
3994 );
3995 }
3996
3997 #[test]
3998 fn test_voter_base_key_can_not_authorize_new_withdrawer_checked() {
3999 let VoteAccountTestFixtureWithAuthorities {
4000 vote_pubkey,
4001 voter_base_key,
4002 voter_owner,
4003 voter_seed,
4004 vote_account,
4005 ..
4006 } = create_test_account_with_authorized_from_seed();
4007 let new_withdrawer_pubkey = Pubkey::new_unique();
4008 let clock = Clock {
4009 epoch: 1,
4010 leader_schedule_epoch: 2,
4011 ..Clock::default()
4012 };
4013 let clock_account = create_sysvar_account(&clock);
4014 let transaction_accounts = vec![
4015 (vote_pubkey, vote_account),
4016 (sysvar::clock::id(), clock_account),
4017 (voter_base_key, AccountSharedData::default()),
4018 (new_withdrawer_pubkey, AccountSharedData::default()),
4019 ];
4020 let instruction_accounts = vec![
4021 AccountMeta {
4022 pubkey: vote_pubkey,
4023 is_signer: false,
4024 is_writable: true,
4025 },
4026 AccountMeta {
4027 pubkey: sysvar::clock::id(),
4028 is_signer: false,
4029 is_writable: false,
4030 },
4031 AccountMeta {
4032 pubkey: voter_base_key,
4033 is_signer: true,
4034 is_writable: false,
4035 },
4036 AccountMeta {
4037 pubkey: new_withdrawer_pubkey,
4038 is_signer: true,
4039 is_writable: false,
4040 },
4041 ];
4042 process_instruction(
4044 VoteProgramFeatures {
4045 ..Default::default()
4046 },
4047 &serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
4048 VoteAuthorizeCheckedWithSeedArgs {
4049 authorization_type: VoteAuthorize::Withdrawer,
4050 current_authority_derived_key_owner: voter_owner,
4051 current_authority_derived_key_seed: voter_seed,
4052 },
4053 ))
4054 .unwrap(),
4055 transaction_accounts,
4056 instruction_accounts,
4057 Err(InstructionError::MissingRequiredSignature),
4058 );
4059 }
4060
4061 #[test]
4062 fn test_withdrawer_base_key_can_authorize_new_withdrawer_checked() {
4063 let VoteAccountTestFixtureWithAuthorities {
4064 vote_pubkey,
4065 withdrawer_base_key,
4066 withdrawer_owner,
4067 withdrawer_seed,
4068 vote_account,
4069 ..
4070 } = create_test_account_with_authorized_from_seed();
4071 let new_withdrawer_pubkey = Pubkey::new_unique();
4072 perform_authorize_checked_with_seed_test(
4073 false,
4074 VoteAuthorize::Withdrawer,
4075 vote_pubkey,
4076 vote_account,
4077 withdrawer_base_key,
4078 withdrawer_seed,
4079 withdrawer_owner,
4080 new_withdrawer_pubkey,
4081 );
4082 }
4083
4084 #[test]
4085 fn test_spoofed_vote() {
4086 let features = VoteProgramFeatures {
4087 ..Default::default()
4088 };
4089 process_instruction_as_one_arg(
4090 features,
4091 &vote(
4092 &invalid_vote_state_pubkey(),
4093 &Pubkey::new_unique(),
4094 Vote::default(),
4095 ),
4096 Err(InstructionError::InvalidAccountOwner),
4097 );
4098 process_instruction_as_one_arg(
4099 features,
4100 &update_vote_state(
4101 &invalid_vote_state_pubkey(),
4102 &Pubkey::default(),
4103 VoteStateUpdate::default(),
4104 ),
4105 Err(InstructionError::InvalidAccountOwner),
4106 );
4107 process_instruction_as_one_arg(
4108 features,
4109 &compact_update_vote_state(
4110 &invalid_vote_state_pubkey(),
4111 &Pubkey::default(),
4112 VoteStateUpdate::default(),
4113 ),
4114 Err(InstructionError::InvalidAccountOwner),
4115 );
4116 process_instruction_as_one_arg(
4117 features,
4118 &tower_sync(
4119 &invalid_vote_state_pubkey(),
4120 &Pubkey::default(),
4121 TowerSync::default(),
4122 ),
4123 Err(InstructionError::InvalidAccountOwner),
4124 );
4125 }
4126
4127 #[test]
4128 fn test_create_account_vote_state_1_14_11() {
4129 let node_pubkey = Pubkey::new_unique();
4130 let vote_pubkey = Pubkey::new_unique();
4131 let instructions = create_account_with_config(
4132 &node_pubkey,
4133 &vote_pubkey,
4134 &VoteInit {
4135 node_pubkey,
4136 authorized_voter: vote_pubkey,
4137 authorized_withdrawer: vote_pubkey,
4138 commission: 0,
4139 },
4140 101,
4141 CreateVoteAccountConfig {
4142 space: vote_state::VoteState1_14_11::size_of() as u64,
4143 ..CreateVoteAccountConfig::default()
4144 },
4145 );
4146 let space = usize::from_le_bytes(instructions[0].data[12..20].try_into().unwrap());
4149 assert_eq!(space, vote_state::VoteState1_14_11::size_of());
4150 let empty_vote_account = AccountSharedData::new(101, space, &id());
4151
4152 let transaction_accounts = vec![
4153 (vote_pubkey, empty_vote_account),
4154 (node_pubkey, AccountSharedData::default()),
4155 (sysvar::clock::id(), create_default_clock_account()),
4156 (sysvar::rent::id(), create_default_rent_account()),
4157 ];
4158
4159 process_instruction(
4161 VoteProgramFeatures {
4162 ..Default::default()
4163 },
4164 &instructions[1].data,
4165 transaction_accounts,
4166 instructions[1].accounts.clone(),
4167 Err(InstructionError::InvalidAccountData),
4168 );
4169 }
4170
4171 #[test]
4172 fn test_create_account_vote_state_current() {
4173 let node_pubkey = Pubkey::new_unique();
4174 let vote_pubkey = Pubkey::new_unique();
4175 let instructions = create_account_with_config(
4176 &node_pubkey,
4177 &vote_pubkey,
4178 &VoteInit {
4179 node_pubkey,
4180 authorized_voter: vote_pubkey,
4181 authorized_withdrawer: vote_pubkey,
4182 commission: 0,
4183 },
4184 101,
4185 CreateVoteAccountConfig {
4186 space: vote_state_size_of() as u64,
4187 ..CreateVoteAccountConfig::default()
4188 },
4189 );
4190 let space = usize::from_le_bytes(instructions[0].data[12..20].try_into().unwrap());
4193 assert_eq!(space, vote_state_size_of());
4194 let empty_vote_account = AccountSharedData::new(101, space, &id());
4195
4196 let transaction_accounts = vec![
4197 (vote_pubkey, empty_vote_account),
4198 (node_pubkey, AccountSharedData::default()),
4199 (sysvar::clock::id(), create_default_clock_account()),
4200 (sysvar::rent::id(), create_default_rent_account()),
4201 ];
4202
4203 process_instruction(
4204 VoteProgramFeatures {
4205 ..Default::default()
4206 },
4207 &instructions[1].data,
4208 transaction_accounts,
4209 instructions[1].accounts.clone(),
4210 Ok(()),
4211 );
4212 }
4213
4214 #[test]
4215 fn test_vote_process_instruction() {
4216 agave_logger::setup();
4217 let instructions = create_account_with_config(
4218 &Pubkey::new_unique(),
4219 &Pubkey::new_unique(),
4220 &VoteInit::default(),
4221 101,
4222 CreateVoteAccountConfig::default(),
4223 );
4224 let features = VoteProgramFeatures {
4225 ..Default::default()
4226 };
4227 process_instruction_as_one_arg(
4230 features,
4231 &instructions[1],
4232 Err(InstructionError::InvalidAccountData),
4233 );
4234 process_instruction_as_one_arg(
4235 features,
4236 &vote(
4237 &Pubkey::new_unique(),
4238 &Pubkey::new_unique(),
4239 Vote::default(),
4240 ),
4241 Err(InstructionError::InvalidInstructionData),
4242 );
4243 process_instruction_as_one_arg(
4244 features,
4245 &vote_switch(
4246 &Pubkey::new_unique(),
4247 &Pubkey::new_unique(),
4248 Vote::default(),
4249 Hash::default(),
4250 ),
4251 Err(InstructionError::InvalidInstructionData),
4252 );
4253 process_instruction_as_one_arg(
4254 features,
4255 &authorize(
4256 &Pubkey::new_unique(),
4257 &Pubkey::new_unique(),
4258 &Pubkey::new_unique(),
4259 VoteAuthorize::Voter,
4260 ),
4261 Err(InstructionError::InvalidAccountData),
4262 );
4263 process_instruction_as_one_arg(
4264 features,
4265 &update_vote_state(
4266 &Pubkey::default(),
4267 &Pubkey::default(),
4268 VoteStateUpdate::default(),
4269 ),
4270 Err(InstructionError::InvalidInstructionData),
4271 );
4272
4273 process_instruction_as_one_arg(
4274 features,
4275 &update_vote_state_switch(
4276 &Pubkey::default(),
4277 &Pubkey::default(),
4278 VoteStateUpdate::default(),
4279 Hash::default(),
4280 ),
4281 Err(InstructionError::InvalidInstructionData),
4282 );
4283 process_instruction_as_one_arg(
4284 features,
4285 &compact_update_vote_state(
4286 &Pubkey::default(),
4287 &Pubkey::default(),
4288 VoteStateUpdate::default(),
4289 ),
4290 Err(InstructionError::InvalidInstructionData),
4291 );
4292 process_instruction_as_one_arg(
4293 features,
4294 &compact_update_vote_state_switch(
4295 &Pubkey::default(),
4296 &Pubkey::default(),
4297 VoteStateUpdate::default(),
4298 Hash::default(),
4299 ),
4300 Err(InstructionError::InvalidInstructionData),
4301 );
4302 process_instruction_as_one_arg(
4303 features,
4304 &tower_sync(&Pubkey::default(), &Pubkey::default(), TowerSync::default()),
4305 Err(InstructionError::InvalidAccountData),
4306 );
4307 process_instruction_as_one_arg(
4308 features,
4309 &tower_sync_switch(
4310 &Pubkey::default(),
4311 &Pubkey::default(),
4312 TowerSync::default(),
4313 Hash::default(),
4314 ),
4315 Err(InstructionError::InvalidAccountData),
4316 );
4317
4318 process_instruction_as_one_arg(
4319 features,
4320 &update_validator_identity(
4321 &Pubkey::new_unique(),
4322 &Pubkey::new_unique(),
4323 &Pubkey::new_unique(),
4324 ),
4325 Err(InstructionError::InvalidAccountData),
4326 );
4327 process_instruction_as_one_arg(
4328 features,
4329 &update_commission(&Pubkey::new_unique(), &Pubkey::new_unique(), 0),
4330 Err(InstructionError::InvalidAccountData),
4331 );
4332
4333 process_instruction_as_one_arg(
4334 features,
4335 &withdraw(
4336 &Pubkey::new_unique(),
4337 &Pubkey::new_unique(),
4338 0,
4339 &Pubkey::new_unique(),
4340 ),
4341 Err(InstructionError::InvalidAccountData),
4342 );
4343 }
4344
4345 #[test]
4346 fn test_tower_sync_rejected_after_alpenglow_migration_succeeds() {
4347 let features = VoteProgramFeatures {
4348 alpenglow_migration_succeeded: true,
4349 ..Default::default()
4350 };
4351
4352 process_instruction_as_one_arg(
4353 features,
4354 &tower_sync(&Pubkey::default(), &Pubkey::default(), TowerSync::default()),
4355 Err(InstructionError::InvalidInstructionData),
4356 );
4357 process_instruction_as_one_arg(
4358 features,
4359 &tower_sync_switch(
4360 &Pubkey::default(),
4361 &Pubkey::default(),
4362 TowerSync::default(),
4363 Hash::default(),
4364 ),
4365 Err(InstructionError::InvalidInstructionData),
4366 );
4367 }
4368
4369 #[test_matrix([false, true])]
4370 fn test_vote_authorize_checked(bls_pubkey_management_in_vote_account: bool) {
4371 let vote_pubkey = Pubkey::new_unique();
4372 let authorized_pubkey = Pubkey::new_unique();
4373 let new_authorized_pubkey = Pubkey::new_unique();
4374
4375 let features = VoteProgramFeatures {
4376 bls_pubkey_management_in_vote_account,
4377 ..Default::default()
4378 };
4379
4380 let (bls_pubkey, bls_proof_of_possession) =
4382 create_bls_pubkey_and_proof_of_possession(&vote_pubkey);
4383 let mut instruction = if bls_pubkey_management_in_vote_account {
4384 authorize_checked(
4385 &vote_pubkey,
4386 &authorized_pubkey,
4387 &new_authorized_pubkey,
4388 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
4389 bls_pubkey,
4390 bls_proof_of_possession,
4391 }),
4392 )
4393 } else {
4394 authorize_checked(
4395 &vote_pubkey,
4396 &authorized_pubkey,
4397 &new_authorized_pubkey,
4398 VoteAuthorize::Voter,
4399 )
4400 };
4401 instruction.accounts = instruction.accounts[0..2].to_vec();
4402 process_instruction_as_one_arg(
4403 features,
4404 &instruction,
4405 Err(InstructionError::MissingAccount),
4406 );
4407
4408 let mut instruction = authorize_checked(
4409 &vote_pubkey,
4410 &authorized_pubkey,
4411 &new_authorized_pubkey,
4412 VoteAuthorize::Withdrawer,
4413 );
4414 instruction.accounts = instruction.accounts[0..2].to_vec();
4415 process_instruction_as_one_arg(
4416 features,
4417 &instruction,
4418 Err(InstructionError::MissingAccount),
4419 );
4420
4421 let mut instruction = if bls_pubkey_management_in_vote_account {
4423 authorize_checked(
4424 &vote_pubkey,
4425 &authorized_pubkey,
4426 &new_authorized_pubkey,
4427 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
4428 bls_pubkey,
4429 bls_proof_of_possession,
4430 }),
4431 )
4432 } else {
4433 authorize_checked(
4434 &vote_pubkey,
4435 &authorized_pubkey,
4436 &new_authorized_pubkey,
4437 VoteAuthorize::Voter,
4438 )
4439 };
4440 instruction.accounts[3] = AccountMeta::new_readonly(new_authorized_pubkey, false);
4441 process_instruction_as_one_arg(
4442 features,
4443 &instruction,
4444 Err(InstructionError::MissingRequiredSignature),
4445 );
4446
4447 let mut instruction = authorize_checked(
4448 &vote_pubkey,
4449 &authorized_pubkey,
4450 &new_authorized_pubkey,
4451 VoteAuthorize::Withdrawer,
4452 );
4453 instruction.accounts[3] = AccountMeta::new_readonly(new_authorized_pubkey, false);
4454 process_instruction_as_one_arg(
4455 features,
4456 &instruction,
4457 Err(InstructionError::MissingRequiredSignature),
4458 );
4459
4460 let default_authorized_pubkey = Pubkey::default();
4462 let vote_account = create_test_account_with_provided_authorized(
4463 &default_authorized_pubkey,
4464 &default_authorized_pubkey,
4465 );
4466 let clock_address = sysvar::clock::id();
4467 let clock_account = create_sysvar_account(&Clock::default());
4468 let authorized_account = create_default_account();
4469 let new_authorized_account = create_default_account();
4470 let transaction_accounts = vec![
4471 (vote_pubkey, vote_account),
4472 (clock_address, clock_account),
4473 (default_authorized_pubkey, authorized_account),
4474 (new_authorized_pubkey, new_authorized_account),
4475 ];
4476 let instruction_accounts = vec![
4477 AccountMeta {
4478 pubkey: vote_pubkey,
4479 is_signer: false,
4480 is_writable: true,
4481 },
4482 AccountMeta {
4483 pubkey: clock_address,
4484 is_signer: false,
4485 is_writable: false,
4486 },
4487 AccountMeta {
4488 pubkey: default_authorized_pubkey,
4489 is_signer: true,
4490 is_writable: false,
4491 },
4492 AccountMeta {
4493 pubkey: new_authorized_pubkey,
4494 is_signer: true,
4495 is_writable: false,
4496 },
4497 ];
4498 let (authorize_type, expected_cus) = if bls_pubkey_management_in_vote_account {
4499 (
4500 VoteAuthorize::VoterWithBLS(VoterWithBLSArgs {
4501 bls_pubkey,
4502 bls_proof_of_possession,
4503 }),
4504 DEFAULT_COMPUTE_UNITS + BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS,
4505 )
4506 } else {
4507 (VoteAuthorize::Voter, DEFAULT_COMPUTE_UNITS)
4508 };
4509 process_instruction_with_cu_check(
4510 features,
4511 &serialize(&VoteInstruction::AuthorizeChecked(authorize_type)).unwrap(),
4512 transaction_accounts.clone(),
4513 instruction_accounts.clone(),
4514 Ok(()),
4515 expected_cus,
4516 );
4517 process_instruction(
4518 features,
4519 &serialize(&VoteInstruction::AuthorizeChecked(
4520 VoteAuthorize::Withdrawer,
4521 ))
4522 .unwrap(),
4523 transaction_accounts,
4524 instruction_accounts,
4525 Ok(()),
4526 );
4527 }
4528
4529 #[test]
4536 fn test_uninitialized_vote_account() {
4537 let vote_pubkey = solana_pubkey::new_rand();
4539 let vote_account = AccountSharedData::new(100, vote_state_size_of(), &id());
4540
4541 let expected_error = InstructionError::InvalidAccountData;
4542
4543 let features = VoteProgramFeatures {
4544 ..Default::default()
4545 };
4546
4547 {
4549 let new_authorized_pubkey = solana_pubkey::new_rand();
4550
4551 let instruction_data = serialize(&VoteInstruction::Authorize(
4552 new_authorized_pubkey,
4553 VoteAuthorize::Voter,
4554 ))
4555 .unwrap();
4556
4557 let transaction_accounts = vec![
4558 (vote_pubkey, vote_account),
4559 (sysvar::clock::id(), create_default_clock_account()),
4560 ];
4561
4562 let instruction_accounts = vec![
4563 AccountMeta {
4564 pubkey: vote_pubkey,
4565 is_signer: true,
4566 is_writable: true,
4567 },
4568 AccountMeta {
4569 pubkey: sysvar::clock::id(),
4570 is_signer: false,
4571 is_writable: false,
4572 },
4573 ];
4574
4575 process_instruction(
4576 features,
4577 &instruction_data,
4578 transaction_accounts,
4579 instruction_accounts,
4580 Err(expected_error.clone()),
4581 );
4582 }
4583
4584 {
4586 let vote_account = AccountSharedData::new(100, vote_state_size_of(), &id());
4587 let current_authority_base_key = Pubkey::new_unique();
4588 let current_authority_owner = Pubkey::new_unique();
4589 let new_authority_pubkey = Pubkey::new_unique();
4590
4591 let instruction_data = serialize(&VoteInstruction::AuthorizeWithSeed(
4592 VoteAuthorizeWithSeedArgs {
4593 authorization_type: VoteAuthorize::Voter,
4594 current_authority_derived_key_owner: current_authority_owner,
4595 current_authority_derived_key_seed: String::from("SEED"),
4596 new_authority: new_authority_pubkey,
4597 },
4598 ))
4599 .unwrap();
4600
4601 let transaction_accounts = vec![
4602 (vote_pubkey, vote_account),
4603 (sysvar::clock::id(), create_default_clock_account()),
4604 (current_authority_base_key, AccountSharedData::default()),
4605 ];
4606
4607 let instruction_accounts = vec![
4608 AccountMeta {
4609 pubkey: vote_pubkey,
4610 is_signer: false,
4611 is_writable: true,
4612 },
4613 AccountMeta {
4614 pubkey: sysvar::clock::id(),
4615 is_signer: false,
4616 is_writable: false,
4617 },
4618 AccountMeta {
4619 pubkey: current_authority_base_key,
4620 is_signer: true,
4621 is_writable: false,
4622 },
4623 ];
4624
4625 process_instruction(
4626 features,
4627 &instruction_data,
4628 transaction_accounts,
4629 instruction_accounts,
4630 Err(expected_error.clone()),
4631 );
4632 }
4633
4634 {
4636 let vote_account = AccountSharedData::new(100, vote_state_size_of(), &id());
4637 let current_authority_base_key = Pubkey::new_unique();
4638 let current_authority_owner = Pubkey::new_unique();
4639 let new_authority_pubkey = Pubkey::new_unique();
4640
4641 let instruction_data = serialize(&VoteInstruction::AuthorizeCheckedWithSeed(
4642 VoteAuthorizeCheckedWithSeedArgs {
4643 authorization_type: VoteAuthorize::Voter,
4644 current_authority_derived_key_owner: current_authority_owner,
4645 current_authority_derived_key_seed: String::from("SEED"),
4646 },
4647 ))
4648 .unwrap();
4649
4650 let transaction_accounts = vec![
4651 (vote_pubkey, vote_account),
4652 (sysvar::clock::id(), create_default_clock_account()),
4653 (current_authority_base_key, AccountSharedData::default()),
4654 (new_authority_pubkey, AccountSharedData::default()),
4655 ];
4656
4657 let instruction_accounts = vec![
4658 AccountMeta {
4659 pubkey: vote_pubkey,
4660 is_signer: false,
4661 is_writable: true,
4662 },
4663 AccountMeta {
4664 pubkey: sysvar::clock::id(),
4665 is_signer: false,
4666 is_writable: false,
4667 },
4668 AccountMeta {
4669 pubkey: current_authority_base_key,
4670 is_signer: true,
4671 is_writable: false,
4672 },
4673 AccountMeta {
4674 pubkey: new_authority_pubkey,
4675 is_signer: true,
4676 is_writable: false,
4677 },
4678 ];
4679
4680 process_instruction(
4681 features,
4682 &instruction_data,
4683 transaction_accounts,
4684 instruction_accounts,
4685 Err(expected_error.clone()),
4686 );
4687 }
4688
4689 {
4691 let vote_account = AccountSharedData::new(100, vote_state_size_of(), &id());
4692 let node_pubkey = Pubkey::new_unique();
4693 let authorized_withdrawer = Pubkey::new_unique();
4694
4695 let instruction_data = serialize(&VoteInstruction::UpdateValidatorIdentity).unwrap();
4696
4697 let transaction_accounts = vec![
4698 (vote_pubkey, vote_account),
4699 (node_pubkey, AccountSharedData::default()),
4700 (authorized_withdrawer, AccountSharedData::default()),
4701 ];
4702
4703 let instruction_accounts = vec![
4704 AccountMeta {
4705 pubkey: vote_pubkey,
4706 is_signer: false,
4707 is_writable: true,
4708 },
4709 AccountMeta {
4710 pubkey: node_pubkey,
4711 is_signer: true,
4712 is_writable: false,
4713 },
4714 AccountMeta {
4715 pubkey: authorized_withdrawer,
4716 is_signer: true,
4717 is_writable: false,
4718 },
4719 ];
4720
4721 process_instruction(
4722 features,
4723 &instruction_data,
4724 transaction_accounts,
4725 instruction_accounts,
4726 Err(expected_error.clone()),
4727 );
4728 }
4729
4730 {
4732 let vote_account = AccountSharedData::new(100, vote_state_size_of(), &id());
4733 let authorized_withdrawer = Pubkey::new_unique();
4734
4735 let instruction_data = serialize(&VoteInstruction::UpdateCommission(42)).unwrap();
4736
4737 let transaction_accounts = vec![
4738 (vote_pubkey, vote_account),
4739 (authorized_withdrawer, AccountSharedData::default()),
4740 (
4741 sysvar::clock::id(),
4742 create_sysvar_account(&Clock::default()),
4743 ),
4744 (
4745 sysvar::epoch_schedule::id(),
4746 create_sysvar_account(&EpochSchedule::without_warmup()),
4747 ),
4748 ];
4749
4750 let instruction_accounts = vec![
4751 AccountMeta {
4752 pubkey: vote_pubkey,
4753 is_signer: false,
4754 is_writable: true,
4755 },
4756 AccountMeta {
4757 pubkey: authorized_withdrawer,
4758 is_signer: true,
4759 is_writable: false,
4760 },
4761 ];
4762
4763 process_instruction(
4764 features,
4765 &instruction_data,
4766 transaction_accounts,
4767 instruction_accounts,
4768 Err(expected_error.clone()),
4769 );
4770 }
4771
4772 {
4774 let vote_account = AccountSharedData::new(100, vote_state_size_of(), &id());
4775 let recipient = Pubkey::new_unique();
4776
4777 let instruction_data = serialize(&VoteInstruction::Withdraw(10)).unwrap();
4778
4779 let transaction_accounts = vec![
4780 (vote_pubkey, vote_account),
4781 (recipient, AccountSharedData::default()),
4782 (sysvar::clock::id(), create_default_clock_account()),
4783 (sysvar::rent::id(), create_default_rent_account()),
4784 ];
4785
4786 let instruction_accounts = vec![
4787 AccountMeta {
4788 pubkey: vote_pubkey,
4789 is_signer: true,
4790 is_writable: true,
4791 },
4792 AccountMeta {
4793 pubkey: recipient,
4794 is_signer: false,
4795 is_writable: true,
4796 },
4797 ];
4798
4799 process_instruction(
4800 features,
4801 &instruction_data,
4802 transaction_accounts,
4803 instruction_accounts,
4804 Err(expected_error.clone()),
4805 );
4806 }
4807
4808 {
4810 let vote_account = AccountSharedData::new(100, vote_state_size_of(), &id());
4811 let authorized_pubkey = Pubkey::new_unique();
4812 let new_authorized_pubkey = Pubkey::new_unique();
4813
4814 let instruction_data =
4815 serialize(&VoteInstruction::AuthorizeChecked(VoteAuthorize::Voter)).unwrap();
4816
4817 let transaction_accounts = vec![
4818 (vote_pubkey, vote_account),
4819 (sysvar::clock::id(), create_default_clock_account()),
4820 (authorized_pubkey, AccountSharedData::default()),
4821 (new_authorized_pubkey, AccountSharedData::default()),
4822 ];
4823
4824 let instruction_accounts = vec![
4825 AccountMeta {
4826 pubkey: vote_pubkey,
4827 is_signer: false,
4828 is_writable: true,
4829 },
4830 AccountMeta {
4831 pubkey: sysvar::clock::id(),
4832 is_signer: false,
4833 is_writable: false,
4834 },
4835 AccountMeta {
4836 pubkey: authorized_pubkey,
4837 is_signer: true,
4838 is_writable: false,
4839 },
4840 AccountMeta {
4841 pubkey: new_authorized_pubkey,
4842 is_signer: true,
4843 is_writable: false,
4844 },
4845 ];
4846
4847 process_instruction(
4848 features,
4849 &instruction_data,
4850 transaction_accounts,
4851 instruction_accounts,
4852 Err(expected_error),
4853 );
4854 }
4855 }
4856
4857 #[test]
4859 fn test_deposit_delegator_rewards() {
4860 const DEPOSIT_DELEGATOR_REWARDS_COMPUTE_UNITS: u64 =
4861 DEFAULT_COMPUTE_UNITS + SYSTEM_PROGRAM_COMPUTE_UNITS;
4862
4863 let (vote_pubkey, _authorized_voter, _authorized_withdrawer, vote_account_v4) =
4864 create_test_account_with_authorized();
4865 let (vote_pubkey_v3, vote_account_v3) = create_test_account_v3();
4866
4867 let source_pubkey = Pubkey::new_unique();
4869 let source_lamports = 1_000_000;
4870 let source_account =
4871 AccountSharedData::new(source_lamports, 0, &solana_sdk_ids::system_program::id());
4872
4873 let deposit_amount = 100_000;
4874
4875 let instruction_data = serialize(&VoteInstruction::DepositDelegatorRewards {
4876 deposit: deposit_amount,
4877 })
4878 .unwrap();
4879
4880 let instruction_accounts = vec![
4881 AccountMeta {
4882 pubkey: vote_pubkey,
4883 is_signer: false,
4884 is_writable: true,
4885 },
4886 AccountMeta {
4887 pubkey: source_pubkey,
4888 is_signer: true,
4889 is_writable: true,
4890 },
4891 AccountMeta {
4892 pubkey: solana_sdk_ids::system_program::id(),
4893 is_signer: false,
4894 is_writable: false,
4895 },
4896 ];
4897
4898 let transaction_accounts = vec![
4899 (vote_pubkey, vote_account_v4.clone()),
4900 (source_pubkey, source_account.clone()),
4901 (
4902 solana_sdk_ids::system_program::id(),
4903 AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id()),
4904 ),
4905 ];
4906
4907 process_instruction(
4909 VoteProgramFeatures {
4910 commission_rate_in_basis_points: false,
4911 custom_commission_collector: true,
4912 block_revenue_sharing: true,
4913 ..Default::default()
4914 },
4915 &instruction_data,
4916 transaction_accounts.clone(),
4917 instruction_accounts.clone(),
4918 Err(InstructionError::InvalidInstructionData),
4919 );
4920
4921 process_instruction(
4923 VoteProgramFeatures {
4924 commission_rate_in_basis_points: true,
4925 custom_commission_collector: false,
4926 block_revenue_sharing: true,
4927 ..Default::default()
4928 },
4929 &instruction_data,
4930 transaction_accounts.clone(),
4931 instruction_accounts.clone(),
4932 Err(InstructionError::InvalidInstructionData),
4933 );
4934
4935 process_instruction(
4937 VoteProgramFeatures {
4938 commission_rate_in_basis_points: true,
4939 custom_commission_collector: true,
4940 block_revenue_sharing: false,
4941 ..Default::default()
4942 },
4943 &instruction_data,
4944 transaction_accounts.clone(),
4945 instruction_accounts.clone(),
4946 Err(InstructionError::InvalidInstructionData),
4947 );
4948
4949 let single_account_instruction_accounts = vec![AccountMeta {
4951 pubkey: vote_pubkey,
4952 is_signer: false,
4953 is_writable: true,
4954 }];
4955 process_instruction(
4956 VoteProgramFeatures::all_enabled(),
4957 &instruction_data,
4958 transaction_accounts.clone(),
4959 single_account_instruction_accounts,
4960 Err(InstructionError::MissingAccount),
4961 );
4962
4963 let non_signer_instruction_accounts = vec![
4965 AccountMeta {
4966 pubkey: vote_pubkey,
4967 is_signer: false,
4968 is_writable: true,
4969 },
4970 AccountMeta {
4971 pubkey: source_pubkey,
4972 is_signer: false,
4973 is_writable: true,
4974 },
4975 AccountMeta {
4976 pubkey: solana_sdk_ids::system_program::id(),
4977 is_signer: false,
4978 is_writable: false,
4979 },
4980 ];
4981 process_instruction(
4982 VoteProgramFeatures::all_enabled(),
4983 &instruction_data,
4984 transaction_accounts.clone(),
4985 non_signer_instruction_accounts,
4986 Err(InstructionError::MissingRequiredSignature),
4987 );
4988
4989 let invalid_vote_account = AccountSharedData::new(1_000_000, VoteStateV4::size_of(), &id());
4991 process_instruction(
4992 VoteProgramFeatures::all_enabled(),
4993 &instruction_data,
4994 vec![
4995 (vote_pubkey, invalid_vote_account),
4996 (source_pubkey, source_account.clone()),
4997 ],
4998 instruction_accounts.clone(),
4999 Err(InstructionError::InvalidAccountData),
5000 );
5001
5002 let instruction_accounts_v3 = vec![
5004 AccountMeta {
5005 pubkey: vote_pubkey_v3,
5006 is_signer: false,
5007 is_writable: true,
5008 },
5009 AccountMeta {
5010 pubkey: source_pubkey,
5011 is_signer: true,
5012 is_writable: true,
5013 },
5014 ];
5015 process_instruction(
5016 VoteProgramFeatures::all_enabled(),
5017 &instruction_data,
5018 vec![
5019 (vote_pubkey_v3, vote_account_v3),
5020 (source_pubkey, source_account.clone()),
5021 ],
5022 instruction_accounts_v3,
5023 Err(InstructionError::InvalidAccountData),
5024 );
5025
5026 let non_system_source_account = AccountSharedData::new(1_000_000, 0, &Pubkey::new_unique());
5028 process_instruction_with_cu_check(
5029 VoteProgramFeatures::all_enabled(),
5030 &instruction_data,
5031 vec![
5032 (vote_pubkey, vote_account_v4.clone()),
5033 (source_pubkey, non_system_source_account),
5034 (
5035 solana_sdk_ids::system_program::id(),
5036 AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id()),
5037 ),
5038 ],
5039 instruction_accounts.clone(),
5040 Err(InstructionError::ExternalAccountLamportSpend),
5041 DEPOSIT_DELEGATOR_REWARDS_COMPUTE_UNITS,
5042 );
5043
5044 process_instruction_with_cu_check(
5046 VoteProgramFeatures::all_enabled(),
5047 &instruction_data,
5048 vec![
5049 (vote_pubkey, vote_account_v4.clone()),
5050 (
5051 solana_sdk_ids::system_program::id(),
5052 AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id()),
5053 ),
5054 ],
5055 vec![
5056 AccountMeta {
5057 pubkey: vote_pubkey,
5058 is_signer: false,
5059 is_writable: true,
5060 },
5061 AccountMeta {
5062 pubkey: vote_pubkey, is_signer: true,
5064 is_writable: true,
5065 },
5066 AccountMeta {
5067 pubkey: solana_sdk_ids::system_program::id(),
5068 is_signer: false,
5069 is_writable: false,
5070 },
5071 ],
5072 Err(InstructionError::InvalidArgument),
5073 DEPOSIT_DELEGATOR_REWARDS_COMPUTE_UNITS,
5074 );
5075
5076 let underfunded_source_account =
5078 AccountSharedData::new(deposit_amount - 1, 0, &solana_sdk_ids::system_program::id());
5079 process_instruction_with_cu_check(
5080 VoteProgramFeatures::all_enabled(),
5081 &instruction_data,
5082 vec![
5083 (vote_pubkey, vote_account_v4.clone()),
5084 (source_pubkey, underfunded_source_account),
5085 (
5086 solana_sdk_ids::system_program::id(),
5087 AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id()),
5088 ),
5089 ],
5090 instruction_accounts.clone(),
5091 Err(InstructionError::Custom(1)),
5093 DEPOSIT_DELEGATOR_REWARDS_COMPUTE_UNITS,
5094 );
5095
5096 let deposit_amount = 100_000;
5098 let mut vote_account_near_max = vote_account_v4.clone();
5099 {
5100 let mut vote_state =
5101 VoteStateV4::deserialize(vote_account_near_max.data(), &vote_pubkey).unwrap();
5102 vote_state.pending_delegator_rewards = u64::MAX - deposit_amount + 1;
5103 vote_account_near_max
5104 .set_data_from_slice(&VoteStateHandler::new_v4(vote_state).serialize());
5105 }
5106
5107 let instruction_data = serialize(&VoteInstruction::DepositDelegatorRewards {
5108 deposit: deposit_amount,
5109 })
5110 .unwrap();
5111
5112 process_instruction_with_cu_check(
5113 VoteProgramFeatures::all_enabled(),
5114 &instruction_data,
5115 vec![
5116 (vote_pubkey, vote_account_near_max),
5117 (source_pubkey, source_account.clone()),
5118 (
5119 solana_sdk_ids::system_program::id(),
5120 AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id()),
5121 ),
5122 ],
5123 instruction_accounts.clone(),
5124 Err(InstructionError::ArithmeticOverflow),
5125 DEPOSIT_DELEGATOR_REWARDS_COMPUTE_UNITS,
5126 );
5127
5128 let resulting_accounts = process_instruction_with_cu_check(
5130 VoteProgramFeatures::all_enabled(),
5131 &instruction_data,
5132 transaction_accounts.clone(),
5133 instruction_accounts.clone(),
5134 Ok(()),
5135 DEPOSIT_DELEGATOR_REWARDS_COMPUTE_UNITS,
5136 );
5137
5138 let vote_account_starting_lamports = vote_account_v4.lamports();
5142 let source_account_starting_lamports = source_lamports;
5143 let resulting_vote_account = &resulting_accounts[0];
5144 let resulting_source_account = &resulting_accounts[1];
5145 let vote_state =
5146 deserialize_vote_state_for_test(resulting_vote_account.data(), &vote_pubkey);
5147 assert_eq!(
5148 resulting_vote_account.lamports(),
5149 vote_account_starting_lamports + deposit_amount,
5150 );
5151 assert_eq!(
5152 resulting_source_account.lamports(),
5153 source_account_starting_lamports - deposit_amount,
5154 );
5155 assert_eq!(
5156 vote_state.as_ref_v4().pending_delegator_rewards,
5157 deposit_amount,
5158 );
5159
5160 let first_deposit_amount = deposit_amount;
5162 let second_deposit_amount = 250_000;
5163 let vote_account_starting_lamports = resulting_vote_account.lamports();
5164 let source_account_starting_lamports = resulting_source_account.lamports();
5165
5166 let instruction_data = serialize(&VoteInstruction::DepositDelegatorRewards {
5167 deposit: second_deposit_amount,
5168 })
5169 .unwrap();
5170
5171 let resulting_accounts = process_instruction_with_cu_check(
5172 VoteProgramFeatures::all_enabled(),
5173 &instruction_data,
5174 vec![
5175 (vote_pubkey, resulting_vote_account.clone()),
5176 (source_pubkey, resulting_source_account.clone()),
5177 (
5178 solana_sdk_ids::system_program::id(),
5179 AccountSharedData::new(0, 0, &solana_sdk_ids::native_loader::id()),
5180 ),
5181 ],
5182 instruction_accounts.clone(),
5183 Ok(()),
5184 DEPOSIT_DELEGATOR_REWARDS_COMPUTE_UNITS,
5185 );
5186
5187 let resulting_vote_account = &resulting_accounts[0];
5188 let resulting_source_account = &resulting_accounts[1];
5189 let vote_state =
5190 deserialize_vote_state_for_test(resulting_vote_account.data(), &vote_pubkey);
5191 assert_eq!(
5192 resulting_vote_account.lamports(),
5193 vote_account_starting_lamports + second_deposit_amount,
5194 );
5195 assert_eq!(
5196 resulting_source_account.lamports(),
5197 source_account_starting_lamports - second_deposit_amount,
5198 );
5199 assert_eq!(
5200 vote_state.as_ref_v4().pending_delegator_rewards,
5201 first_deposit_amount + second_deposit_amount,
5202 );
5203
5204 let vote_account_starting_lamports = vote_account_v4.lamports();
5206 let source_account_starting_lamports = source_lamports;
5207 let instruction_data =
5208 serialize(&VoteInstruction::DepositDelegatorRewards { deposit: 0 }).unwrap();
5209
5210 let resulting_accounts = process_instruction_with_cu_check(
5211 VoteProgramFeatures::all_enabled(),
5212 &instruction_data,
5213 transaction_accounts,
5214 instruction_accounts.clone(),
5215 Ok(()),
5216 DEPOSIT_DELEGATOR_REWARDS_COMPUTE_UNITS,
5217 );
5218
5219 let resulting_vote_account = &resulting_accounts[0];
5220 let resulting_source_account = &resulting_accounts[1];
5221 let vote_state =
5222 deserialize_vote_state_for_test(resulting_vote_account.data(), &vote_pubkey);
5223 assert_eq!(
5224 resulting_vote_account.lamports(),
5225 vote_account_starting_lamports, );
5227 assert_eq!(
5228 resulting_source_account.lamports(),
5229 source_account_starting_lamports, );
5231 assert_eq!(
5232 vote_state.as_ref_v4().pending_delegator_rewards,
5233 0, );
5235 }
5236
5237 #[test]
5238 #[allow(clippy::arithmetic_side_effects)]
5239 fn test_withdraw_pending_delegator_rewards() {
5240 let rent_sysvar = Rent::default();
5241 let rent_minimum_balance = rent_sysvar.minimum_balance(VoteStateV4::size_of());
5242
5243 let pending_rewards = 500_000;
5244 let extra_for_withdraw = 100_000;
5245 let vote_account_lamports = rent_minimum_balance + pending_rewards + extra_for_withdraw;
5246
5247 let (vote_pubkey, _authorized_voter, authorized_withdrawer, mut vote_account) =
5248 create_test_account_with_authorized();
5249
5250 {
5252 let mut vote_state =
5253 VoteStateV4::deserialize(vote_account.data(), &vote_pubkey).unwrap();
5254 vote_state.pending_delegator_rewards = pending_rewards;
5255 vote_account.set_data_from_slice(&VoteStateHandler::new_v4(vote_state).serialize());
5256 vote_account.set_lamports(vote_account_lamports);
5257 };
5258
5259 let features = VoteProgramFeatures::all_enabled();
5260
5261 let instruction_accounts = vec![
5262 AccountMeta {
5263 pubkey: vote_pubkey,
5264 is_signer: false,
5265 is_writable: true,
5266 },
5267 AccountMeta {
5268 pubkey: authorized_withdrawer,
5269 is_signer: true,
5270 is_writable: true,
5271 },
5272 ];
5273
5274 let rent_account = create_sysvar_account(&rent_sysvar);
5275 let transaction_accounts = vec![
5276 (vote_pubkey, vote_account.clone()),
5277 (authorized_withdrawer, AccountSharedData::default()),
5278 (sysvar::clock::id(), create_default_clock_account()),
5279 (sysvar::rent::id(), rent_account.clone()),
5280 ];
5281
5282 process_instruction(
5285 features,
5286 &serialize(&VoteInstruction::Withdraw(vote_account_lamports)).unwrap(),
5287 transaction_accounts.clone(),
5288 instruction_accounts.clone(),
5289 Err(InstructionError::InsufficientFunds),
5290 );
5291
5292 process_instruction(
5295 features,
5296 &serialize(&VoteInstruction::Withdraw(vote_account_lamports + 1)).unwrap(),
5297 transaction_accounts.clone(),
5298 instruction_accounts.clone(),
5299 Err(InstructionError::InsufficientFunds),
5300 );
5301
5302 for i in 1..10 {
5304 let withdraw_amount = 1 + i * extra_for_withdraw / 10;
5305
5306 let accounts = process_instruction(
5307 features,
5308 &serialize(&VoteInstruction::Withdraw(withdraw_amount)).unwrap(),
5309 transaction_accounts.clone(),
5310 instruction_accounts.clone(),
5311 Ok(()),
5312 );
5313
5314 assert_eq!(
5315 accounts[0].lamports(),
5316 vote_account_lamports - withdraw_amount
5317 );
5318 assert!(accounts[0].lamports() >= rent_minimum_balance + pending_rewards);
5319 assert_eq!(accounts[1].lamports(), withdraw_amount);
5320 }
5321
5322 {
5324 let mut vote_state =
5325 VoteStateV4::deserialize(vote_account.data(), &vote_pubkey).unwrap();
5326 vote_state.pending_delegator_rewards = 0;
5327 vote_account.set_data_from_slice(&VoteStateHandler::new_v4(vote_state).serialize());
5328 vote_account.set_lamports(vote_account_lamports);
5329 };
5330
5331 let accounts = process_instruction(
5334 features,
5335 &serialize(&VoteInstruction::Withdraw(vote_account_lamports)).unwrap(),
5336 vec![
5337 (vote_pubkey, vote_account.clone()),
5338 (authorized_withdrawer, AccountSharedData::default()),
5339 (sysvar::clock::id(), create_default_clock_account()),
5340 (sysvar::rent::id(), rent_account),
5341 ],
5342 instruction_accounts.clone(),
5343 Ok(()),
5344 );
5345
5346 assert_eq!(accounts[0].lamports(), 0);
5347 assert_eq!(accounts[0].data(), vec![0; VoteStateV4::size_of()]);
5348 assert_eq!(accounts[1].lamports(), vote_account_lamports);
5349 }
5350}