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