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