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