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