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