1#[cfg(feature = "frozen-abi")]
4use solana_frozen_abi_macro::{frozen_abi, StableAbi, StableAbiSample};
5use {
6 super::state::TowerSync,
7 crate::state::{
8 Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs, VoteAuthorizeWithSeedArgs, VoteInit,
9 VoteInitV2, VoteStateUpdate, VoteStateV4,
10 },
11 solana_clock::{Slot, UnixTimestamp},
12 solana_hash::Hash,
13 solana_pubkey::Pubkey,
14};
15#[cfg(feature = "bincode")]
16use {
17 crate::program::id,
18 solana_instruction::{AccountMeta, Instruction},
19 solana_sdk_ids::{system_program, sysvar},
20};
21#[cfg(feature = "wincode")]
22use {
23 crate::state::wincode_compact::{CompactTowerSync, CompactVoteStateUpdate},
24 wincode::{SchemaRead, SchemaWrite},
25};
26#[cfg(feature = "serde")]
27use {
28 crate::state::{serde_compact_vote_state_update, serde_tower_sync},
29 serde_derive::{Deserialize, Serialize},
30};
31
32#[repr(u8)]
33#[cfg_attr(feature = "frozen-abi", derive(StableAbi, StableAbiSample))]
34#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
35#[cfg_attr(feature = "wincode", derive(SchemaWrite, SchemaRead))]
36#[derive(Debug, PartialEq, Eq, Clone)]
37pub enum CommissionKind {
38 InflationRewards = 0,
39 BlockRevenue = 1,
40}
41
42#[cfg_attr(
43 feature = "frozen-abi",
44 frozen_abi(
45 abi_digest = "9bqZ5L1KnMFnjQPMoRtfhdgUok3G5W2KKRGuw8uwbkuY",
46 abi_serializer = ["bincode", "wincode"]
47 ),
48 derive(StableAbi, StableAbiSample)
49)]
50#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
51#[cfg_attr(feature = "wincode", derive(SchemaWrite, SchemaRead))]
52#[derive(Debug, PartialEq, Eq, Clone)]
53pub enum VoteInstruction {
54 InitializeAccount(VoteInit),
62
63 Authorize(Pubkey, VoteAuthorize),
73
74 Vote(Vote),
82
83 Withdraw(u64),
90
91 UpdateValidatorIdentity,
98
99 UpdateCommission(u8),
105
106 VoteSwitch(Vote, Hash),
114
115 AuthorizeChecked(VoteAuthorize),
129
130 UpdateVoteState(VoteStateUpdate),
136
137 UpdateVoteStateSwitch(VoteStateUpdate, Hash),
143
144 AuthorizeWithSeed(VoteAuthorizeWithSeedArgs),
157
158 AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs),
175
176 #[cfg_attr(feature = "serde", serde(with = "serde_compact_vote_state_update"))]
182 CompactUpdateVoteState(
183 #[cfg_attr(feature = "wincode", wincode(with = "CompactVoteStateUpdate"))] VoteStateUpdate,
184 ),
185
186 CompactUpdateVoteStateSwitch(
192 #[cfg_attr(feature = "serde", serde(with = "serde_compact_vote_state_update"))]
193 #[cfg_attr(feature = "wincode", wincode(with = "CompactVoteStateUpdate"))]
194 VoteStateUpdate,
195 Hash,
196 ),
197
198 #[cfg_attr(feature = "serde", serde(with = "serde_tower_sync"))]
204 TowerSync(#[cfg_attr(feature = "wincode", wincode(with = "CompactTowerSync"))] TowerSync),
205
206 TowerSyncSwitch(
212 #[cfg_attr(feature = "serde", serde(with = "serde_tower_sync"))]
213 #[cfg_attr(feature = "wincode", wincode(with = "CompactTowerSync"))]
214 TowerSync,
215 Hash,
216 ),
217
218 InitializeAccountV2(VoteInitV2),
234
235 UpdateCommissionCollector(CommissionKind),
244
245 UpdateCommissionBps {
252 commission_bps: u16,
253 kind: CommissionKind,
254 },
255
256 DepositDelegatorRewards { deposit: u64 },
263}
264
265impl VoteInstruction {
266 pub fn is_simple_vote(&self) -> bool {
267 matches!(
268 self,
269 Self::Vote(_)
270 | Self::VoteSwitch(_, _)
271 | Self::UpdateVoteState(_)
272 | Self::UpdateVoteStateSwitch(_, _)
273 | Self::CompactUpdateVoteState(_)
274 | Self::CompactUpdateVoteStateSwitch(_, _)
275 | Self::TowerSync(_)
276 | Self::TowerSyncSwitch(_, _),
277 )
278 }
279
280 pub fn is_single_vote_state_update(&self) -> bool {
281 matches!(
282 self,
283 Self::UpdateVoteState(_)
284 | Self::UpdateVoteStateSwitch(_, _)
285 | Self::CompactUpdateVoteState(_)
286 | Self::CompactUpdateVoteStateSwitch(_, _)
287 | Self::TowerSync(_)
288 | Self::TowerSyncSwitch(_, _),
289 )
290 }
291
292 pub fn last_voted_slot(&self) -> Option<Slot> {
294 assert!(self.is_simple_vote());
295 match self {
296 Self::Vote(v) | Self::VoteSwitch(v, _) => v.last_voted_slot(),
297 Self::UpdateVoteState(vote_state_update)
298 | Self::UpdateVoteStateSwitch(vote_state_update, _)
299 | Self::CompactUpdateVoteState(vote_state_update)
300 | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
301 vote_state_update.last_voted_slot()
302 }
303 Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => {
304 tower_sync.last_voted_slot()
305 }
306 _ => panic!("Tried to get slot on non simple vote instruction"),
307 }
308 }
309
310 pub fn hash(&self) -> Hash {
312 assert!(self.is_simple_vote());
313 let hash = match self {
314 Self::Vote(v) | Self::VoteSwitch(v, _) => &v.hash,
315 Self::UpdateVoteState(vote_state_update)
316 | Self::UpdateVoteStateSwitch(vote_state_update, _)
317 | Self::CompactUpdateVoteState(vote_state_update)
318 | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => &vote_state_update.hash,
319 Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => &tower_sync.hash,
320 _ => panic!("Tried to get hash on non simple vote instruction"),
321 };
322 Hash::new_from_array(hash.to_bytes())
323 }
324 pub fn timestamp(&self) -> Option<UnixTimestamp> {
326 assert!(self.is_simple_vote());
327 match self {
328 Self::Vote(v) | Self::VoteSwitch(v, _) => v.timestamp,
329 Self::UpdateVoteState(vote_state_update)
330 | Self::UpdateVoteStateSwitch(vote_state_update, _)
331 | Self::CompactUpdateVoteState(vote_state_update)
332 | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
333 vote_state_update.timestamp
334 }
335 Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => {
336 tower_sync.timestamp
337 }
338 _ => panic!("Tried to get timestamp on non simple vote instruction"),
339 }
340 }
341}
342
343#[cfg(feature = "bincode")]
344fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction {
345 let account_metas = vec![
346 AccountMeta::new(*vote_pubkey, false),
347 AccountMeta::new_readonly(sysvar::rent::id(), false),
348 AccountMeta::new_readonly(sysvar::clock::id(), false),
349 AccountMeta::new_readonly(vote_init.node_pubkey, true),
350 ];
351
352 Instruction::new_with_bincode(
353 id(),
354 &VoteInstruction::InitializeAccount(*vote_init),
355 account_metas,
356 )
357}
358
359#[cfg(feature = "bincode")]
360fn initialize_account_v2(
361 vote_pubkey: &Pubkey,
362 vote_init: &VoteInitV2,
363 inflation_rewards_collector: &Pubkey,
364 block_revenue_collector: &Pubkey,
365) -> Instruction {
366 let account_metas = vec![
367 AccountMeta::new(*vote_pubkey, false),
368 AccountMeta::new_readonly(vote_init.node_pubkey, true),
369 AccountMeta::new(*inflation_rewards_collector, false),
370 AccountMeta::new(*block_revenue_collector, false),
371 ];
372
373 Instruction::new_with_bincode(
374 id(),
375 &VoteInstruction::InitializeAccountV2(*vote_init),
376 account_metas,
377 )
378}
379
380pub struct CreateVoteAccountConfig<'a> {
381 pub space: u64,
382 pub with_seed: Option<(&'a Pubkey, &'a str)>,
383}
384
385impl Default for CreateVoteAccountConfig<'_> {
386 fn default() -> Self {
387 Self {
388 space: VoteStateV4::size_of() as u64,
390 with_seed: None,
391 }
392 }
393}
394
395#[cfg(feature = "bincode")]
396pub fn create_account_with_config(
397 from_pubkey: &Pubkey,
398 vote_pubkey: &Pubkey,
399 vote_init: &VoteInit,
400 lamports: u64,
401 config: CreateVoteAccountConfig,
402) -> Vec<Instruction> {
403 let create_ix = if let Some((base, seed)) = config.with_seed {
404 solana_system_interface::instruction::create_account_with_seed(
405 from_pubkey,
406 vote_pubkey,
407 base,
408 seed,
409 lamports,
410 config.space,
411 &id(),
412 )
413 } else {
414 solana_system_interface::instruction::create_account(
415 from_pubkey,
416 vote_pubkey,
417 lamports,
418 config.space,
419 &id(),
420 )
421 };
422 let init_ix = initialize_account(vote_pubkey, vote_init);
423 vec![create_ix, init_ix]
424}
425
426#[cfg(feature = "bincode")]
427pub fn create_account_with_config_v2(
428 from_pubkey: &Pubkey,
429 vote_pubkey: &Pubkey,
430 vote_init: &VoteInitV2,
431 inflation_rewards_collector: &Pubkey,
432 block_revenue_collector: &Pubkey,
433 lamports: u64,
434 config: CreateVoteAccountConfig,
435) -> Vec<Instruction> {
436 let create_ix = if let Some((base, seed)) = config.with_seed {
437 solana_system_interface::instruction::create_account_with_seed(
438 from_pubkey,
439 vote_pubkey,
440 base,
441 seed,
442 lamports,
443 config.space,
444 &id(),
445 )
446 } else {
447 solana_system_interface::instruction::create_account(
448 from_pubkey,
449 vote_pubkey,
450 lamports,
451 config.space,
452 &id(),
453 )
454 };
455 let init_ix = initialize_account_v2(
456 vote_pubkey,
457 vote_init,
458 inflation_rewards_collector,
459 block_revenue_collector,
460 );
461 vec![create_ix, init_ix]
462}
463
464#[cfg(feature = "bincode")]
465pub fn authorize(
466 vote_pubkey: &Pubkey,
467 authorized_pubkey: &Pubkey, new_authorized_pubkey: &Pubkey,
469 vote_authorize: VoteAuthorize,
470) -> Instruction {
471 let account_metas = vec![
472 AccountMeta::new(*vote_pubkey, false),
473 AccountMeta::new_readonly(sysvar::clock::id(), false),
474 AccountMeta::new_readonly(*authorized_pubkey, true),
475 ];
476
477 Instruction::new_with_bincode(
478 id(),
479 &VoteInstruction::Authorize(*new_authorized_pubkey, vote_authorize),
480 account_metas,
481 )
482}
483
484#[cfg(feature = "bincode")]
485pub fn authorize_checked(
486 vote_pubkey: &Pubkey,
487 authorized_pubkey: &Pubkey, new_authorized_pubkey: &Pubkey,
489 vote_authorize: VoteAuthorize,
490) -> Instruction {
491 let account_metas = vec![
492 AccountMeta::new(*vote_pubkey, false),
493 AccountMeta::new_readonly(sysvar::clock::id(), false),
494 AccountMeta::new_readonly(*authorized_pubkey, true),
495 AccountMeta::new_readonly(*new_authorized_pubkey, true),
496 ];
497
498 Instruction::new_with_bincode(
499 id(),
500 &VoteInstruction::AuthorizeChecked(vote_authorize),
501 account_metas,
502 )
503}
504
505#[cfg(feature = "bincode")]
506pub fn authorize_with_seed(
507 vote_pubkey: &Pubkey,
508 current_authority_base_key: &Pubkey,
509 current_authority_derived_key_owner: &Pubkey,
510 current_authority_derived_key_seed: &str,
511 new_authority: &Pubkey,
512 authorization_type: VoteAuthorize,
513) -> Instruction {
514 let account_metas = vec![
515 AccountMeta::new(*vote_pubkey, false),
516 AccountMeta::new_readonly(sysvar::clock::id(), false),
517 AccountMeta::new_readonly(*current_authority_base_key, true),
518 ];
519
520 Instruction::new_with_bincode(
521 id(),
522 &VoteInstruction::AuthorizeWithSeed(VoteAuthorizeWithSeedArgs {
523 authorization_type,
524 current_authority_derived_key_owner: *current_authority_derived_key_owner,
525 current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
526 new_authority: *new_authority,
527 }),
528 account_metas,
529 )
530}
531
532#[cfg(feature = "bincode")]
533pub fn authorize_checked_with_seed(
534 vote_pubkey: &Pubkey,
535 current_authority_base_key: &Pubkey,
536 current_authority_derived_key_owner: &Pubkey,
537 current_authority_derived_key_seed: &str,
538 new_authority: &Pubkey,
539 authorization_type: VoteAuthorize,
540) -> Instruction {
541 let account_metas = vec![
542 AccountMeta::new(*vote_pubkey, false),
543 AccountMeta::new_readonly(sysvar::clock::id(), false),
544 AccountMeta::new_readonly(*current_authority_base_key, true),
545 AccountMeta::new_readonly(*new_authority, true),
546 ];
547
548 Instruction::new_with_bincode(
549 id(),
550 &VoteInstruction::AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs {
551 authorization_type,
552 current_authority_derived_key_owner: *current_authority_derived_key_owner,
553 current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
554 }),
555 account_metas,
556 )
557}
558
559#[cfg(feature = "bincode")]
560pub fn update_validator_identity(
561 vote_pubkey: &Pubkey,
562 authorized_withdrawer_pubkey: &Pubkey,
563 node_pubkey: &Pubkey,
564) -> Instruction {
565 let account_metas = vec![
566 AccountMeta::new(*vote_pubkey, false),
567 AccountMeta::new_readonly(*node_pubkey, true),
568 AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
569 ];
570
571 Instruction::new_with_bincode(
572 id(),
573 &VoteInstruction::UpdateValidatorIdentity,
574 account_metas,
575 )
576}
577
578#[cfg(feature = "bincode")]
579pub fn update_commission(
580 vote_pubkey: &Pubkey,
581 authorized_withdrawer_pubkey: &Pubkey,
582 commission: u8,
583) -> Instruction {
584 let account_metas = vec![
585 AccountMeta::new(*vote_pubkey, false),
586 AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
587 ];
588
589 Instruction::new_with_bincode(
590 id(),
591 &VoteInstruction::UpdateCommission(commission),
592 account_metas,
593 )
594}
595
596#[cfg(feature = "bincode")]
597pub fn update_commission_collector(
598 vote_pubkey: &Pubkey,
599 authorized_withdrawer_pubkey: &Pubkey,
600 new_collector_pubkey: &Pubkey,
601 kind: CommissionKind,
602) -> Instruction {
603 let account_metas = vec![
604 AccountMeta::new(*vote_pubkey, false),
605 AccountMeta::new(*new_collector_pubkey, false),
606 AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
607 ];
608
609 Instruction::new_with_bincode(
610 id(),
611 &VoteInstruction::UpdateCommissionCollector(kind),
612 account_metas,
613 )
614}
615
616#[cfg(feature = "bincode")]
617pub fn update_commission_bps(
618 vote_pubkey: &Pubkey,
619 authorized_withdrawer_pubkey: &Pubkey,
620 kind: CommissionKind,
621 commission_bps: u16,
622) -> Instruction {
623 let account_metas = vec![
624 AccountMeta::new(*vote_pubkey, false),
625 AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
626 ];
627
628 Instruction::new_with_bincode(
629 id(),
630 &VoteInstruction::UpdateCommissionBps {
631 kind,
632 commission_bps,
633 },
634 account_metas,
635 )
636}
637
638#[cfg(feature = "bincode")]
639pub fn deposit_delegator_rewards(
640 vote_pubkey: &Pubkey,
641 source_pubkey: &Pubkey,
642 deposit: u64,
643) -> Instruction {
644 let account_metas = vec![
645 AccountMeta::new(*vote_pubkey, false),
646 AccountMeta::new(*source_pubkey, true),
647 AccountMeta::new_readonly(system_program::id(), false),
648 ];
649
650 Instruction::new_with_bincode(
651 id(),
652 &VoteInstruction::DepositDelegatorRewards { deposit },
653 account_metas,
654 )
655}
656
657#[cfg(feature = "bincode")]
658pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction {
659 let account_metas = vec![
660 AccountMeta::new(*vote_pubkey, false),
661 AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
662 AccountMeta::new_readonly(sysvar::clock::id(), false),
663 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
664 ];
665
666 Instruction::new_with_bincode(id(), &VoteInstruction::Vote(vote), account_metas)
667}
668
669#[cfg(feature = "bincode")]
670pub fn vote_switch(
671 vote_pubkey: &Pubkey,
672 authorized_voter_pubkey: &Pubkey,
673 vote: Vote,
674 proof_hash: Hash,
675) -> Instruction {
676 let account_metas = vec![
677 AccountMeta::new(*vote_pubkey, false),
678 AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
679 AccountMeta::new_readonly(sysvar::clock::id(), false),
680 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
681 ];
682
683 Instruction::new_with_bincode(
684 id(),
685 &VoteInstruction::VoteSwitch(vote, proof_hash),
686 account_metas,
687 )
688}
689
690#[cfg(feature = "bincode")]
691pub fn update_vote_state(
692 vote_pubkey: &Pubkey,
693 authorized_voter_pubkey: &Pubkey,
694 vote_state_update: VoteStateUpdate,
695) -> Instruction {
696 let account_metas = vec![
697 AccountMeta::new(*vote_pubkey, false),
698 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
699 ];
700
701 Instruction::new_with_bincode(
702 id(),
703 &VoteInstruction::UpdateVoteState(vote_state_update),
704 account_metas,
705 )
706}
707
708#[cfg(feature = "bincode")]
709pub fn update_vote_state_switch(
710 vote_pubkey: &Pubkey,
711 authorized_voter_pubkey: &Pubkey,
712 vote_state_update: VoteStateUpdate,
713 proof_hash: Hash,
714) -> Instruction {
715 let account_metas = vec![
716 AccountMeta::new(*vote_pubkey, false),
717 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
718 ];
719
720 Instruction::new_with_bincode(
721 id(),
722 &VoteInstruction::UpdateVoteStateSwitch(vote_state_update, proof_hash),
723 account_metas,
724 )
725}
726
727#[cfg(feature = "bincode")]
728pub fn compact_update_vote_state(
729 vote_pubkey: &Pubkey,
730 authorized_voter_pubkey: &Pubkey,
731 vote_state_update: VoteStateUpdate,
732) -> Instruction {
733 let account_metas = vec![
734 AccountMeta::new(*vote_pubkey, false),
735 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
736 ];
737
738 Instruction::new_with_bincode(
739 id(),
740 &VoteInstruction::CompactUpdateVoteState(vote_state_update),
741 account_metas,
742 )
743}
744
745#[cfg(feature = "bincode")]
746pub fn compact_update_vote_state_switch(
747 vote_pubkey: &Pubkey,
748 authorized_voter_pubkey: &Pubkey,
749 vote_state_update: VoteStateUpdate,
750 proof_hash: Hash,
751) -> Instruction {
752 let account_metas = vec![
753 AccountMeta::new(*vote_pubkey, false),
754 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
755 ];
756
757 Instruction::new_with_bincode(
758 id(),
759 &VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, proof_hash),
760 account_metas,
761 )
762}
763
764#[cfg(feature = "bincode")]
765pub fn tower_sync(
766 vote_pubkey: &Pubkey,
767 authorized_voter_pubkey: &Pubkey,
768 tower_sync: TowerSync,
769) -> Instruction {
770 let account_metas = vec![
771 AccountMeta::new(*vote_pubkey, false),
772 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
773 ];
774
775 Instruction::new_with_bincode(id(), &VoteInstruction::TowerSync(tower_sync), account_metas)
776}
777
778#[cfg(feature = "bincode")]
779pub fn tower_sync_switch(
780 vote_pubkey: &Pubkey,
781 authorized_voter_pubkey: &Pubkey,
782 tower_sync: TowerSync,
783 proof_hash: Hash,
784) -> Instruction {
785 let account_metas = vec![
786 AccountMeta::new(*vote_pubkey, false),
787 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
788 ];
789
790 Instruction::new_with_bincode(
791 id(),
792 &VoteInstruction::TowerSyncSwitch(tower_sync, proof_hash),
793 account_metas,
794 )
795}
796
797#[cfg(feature = "bincode")]
798pub fn withdraw(
799 vote_pubkey: &Pubkey,
800 authorized_withdrawer_pubkey: &Pubkey,
801 lamports: u64,
802 to_pubkey: &Pubkey,
803) -> Instruction {
804 let account_metas = vec![
805 AccountMeta::new(*vote_pubkey, false),
806 AccountMeta::new(*to_pubkey, false),
807 AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
808 ];
809
810 Instruction::new_with_bincode(id(), &VoteInstruction::Withdraw(lamports), account_metas)
811}
812
813#[cfg(all(test, feature = "bincode"))]
814mod tests {
815 use {super::*, crate::state::Lockout, std::collections::VecDeque};
816
817 #[test]
818 fn test_compact_serialize_rejects_confirmation_count_above_u8() {
819 let lockouts = VecDeque::from([Lockout::new_with_confirmation_count(1, 256)]);
823 let vote_state_update = VoteStateUpdate::new(lockouts.clone(), None, Hash::default());
824 let tower_sync = TowerSync::new(lockouts, None, Hash::default(), Hash::default());
825
826 for ix in [
827 VoteInstruction::CompactUpdateVoteState(vote_state_update),
828 VoteInstruction::TowerSync(tower_sync),
829 ] {
830 let err = bincode::serialize(&ix).unwrap_err();
831 assert!(
832 err.to_string().contains("Invalid confirmation count"),
833 "unexpected error: {err}"
834 );
835 #[cfg(feature = "wincode")]
836 assert!(wincode::serialize(&ix).is_err());
837 }
838 }
839
840 #[test]
841 fn test_deposit_delegator_rewards_includes_system_program() {
842 let vote_pubkey = Pubkey::new_unique();
843 let source_pubkey = Pubkey::new_unique();
844
845 let instruction = deposit_delegator_rewards(&vote_pubkey, &source_pubkey, 100_000);
846
847 assert_eq!(instruction.program_id, id());
848 assert_eq!(instruction.accounts.len(), 3);
849
850 let vote_meta = &instruction.accounts[0];
851 assert_eq!(vote_meta.pubkey, vote_pubkey);
852 assert!(!vote_meta.is_signer);
853 assert!(vote_meta.is_writable);
854
855 let source_meta = &instruction.accounts[1];
856 assert_eq!(source_meta.pubkey, source_pubkey);
857 assert!(source_meta.is_signer);
858 assert!(source_meta.is_writable);
859
860 let system_program_meta = &instruction.accounts[2];
863 assert_eq!(system_program_meta.pubkey, system_program::id());
864 assert!(!system_program_meta.is_signer);
865 assert!(!system_program_meta.is_writable);
866 }
867}