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