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