1use {
4 super::state::TowerSync,
5 crate::state::{
6 Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs, VoteAuthorizeWithSeedArgs, VoteInit,
7 VoteInitV2, VoteStateUpdate, VoteStateV4,
8 },
9 solana_clock::{Slot, UnixTimestamp},
10 solana_hash::Hash,
11 solana_pubkey::Pubkey,
12};
13#[cfg(feature = "bincode")]
14use {
15 crate::program::id,
16 solana_instruction::{AccountMeta, Instruction},
17 solana_sdk_ids::sysvar,
18};
19#[cfg(feature = "serde")]
20use {
21 crate::state::{serde_compact_vote_state_update, serde_tower_sync},
22 serde_derive::{Deserialize, Serialize},
23};
24
25#[repr(u8)]
26#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
27#[derive(Debug, PartialEq, Eq, Clone)]
28pub enum CommissionKind {
29 InflationRewards = 0,
30 BlockRevenue = 1,
31}
32
33#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
34#[derive(Debug, PartialEq, Eq, Clone)]
35pub enum VoteInstruction {
36 InitializeAccount(VoteInit),
44
45 Authorize(Pubkey, VoteAuthorize),
55
56 Vote(Vote),
64
65 Withdraw(u64),
72
73 UpdateValidatorIdentity,
80
81 UpdateCommission(u8),
87
88 VoteSwitch(Vote, Hash),
96
97 AuthorizeChecked(VoteAuthorize),
111
112 UpdateVoteState(VoteStateUpdate),
118
119 UpdateVoteStateSwitch(VoteStateUpdate, Hash),
125
126 AuthorizeWithSeed(VoteAuthorizeWithSeedArgs),
139
140 AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs),
157
158 #[cfg_attr(feature = "serde", serde(with = "serde_compact_vote_state_update"))]
164 CompactUpdateVoteState(VoteStateUpdate),
165
166 CompactUpdateVoteStateSwitch(
172 #[cfg_attr(feature = "serde", serde(with = "serde_compact_vote_state_update"))]
173 VoteStateUpdate,
174 Hash,
175 ),
176
177 #[cfg_attr(feature = "serde", serde(with = "serde_tower_sync"))]
183 TowerSync(TowerSync),
184
185 TowerSyncSwitch(
191 #[cfg_attr(feature = "serde", serde(with = "serde_tower_sync"))] TowerSync,
192 Hash,
193 ),
194
195 InitializeAccountV2(VoteInitV2),
211
212 UpdateCommissionCollector(CommissionKind),
221
222 UpdateCommissionBps {
229 commission_bps: u16,
230 kind: CommissionKind,
231 },
232
233 DepositDelegatorRewards { deposit: u64 },
239}
240
241impl VoteInstruction {
242 pub fn is_simple_vote(&self) -> bool {
243 matches!(
244 self,
245 Self::Vote(_)
246 | Self::VoteSwitch(_, _)
247 | Self::UpdateVoteState(_)
248 | Self::UpdateVoteStateSwitch(_, _)
249 | Self::CompactUpdateVoteState(_)
250 | Self::CompactUpdateVoteStateSwitch(_, _)
251 | Self::TowerSync(_)
252 | Self::TowerSyncSwitch(_, _),
253 )
254 }
255
256 pub fn is_single_vote_state_update(&self) -> bool {
257 matches!(
258 self,
259 Self::UpdateVoteState(_)
260 | Self::UpdateVoteStateSwitch(_, _)
261 | Self::CompactUpdateVoteState(_)
262 | Self::CompactUpdateVoteStateSwitch(_, _)
263 | Self::TowerSync(_)
264 | Self::TowerSyncSwitch(_, _),
265 )
266 }
267
268 pub fn last_voted_slot(&self) -> Option<Slot> {
270 assert!(self.is_simple_vote());
271 match self {
272 Self::Vote(v) | Self::VoteSwitch(v, _) => v.last_voted_slot(),
273 Self::UpdateVoteState(vote_state_update)
274 | Self::UpdateVoteStateSwitch(vote_state_update, _)
275 | Self::CompactUpdateVoteState(vote_state_update)
276 | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
277 vote_state_update.last_voted_slot()
278 }
279 Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => {
280 tower_sync.last_voted_slot()
281 }
282 _ => panic!("Tried to get slot on non simple vote instruction"),
283 }
284 }
285
286 pub fn hash(&self) -> Hash {
288 assert!(self.is_simple_vote());
289 let hash = match self {
290 Self::Vote(v) | Self::VoteSwitch(v, _) => &v.hash,
291 Self::UpdateVoteState(vote_state_update)
292 | Self::UpdateVoteStateSwitch(vote_state_update, _)
293 | Self::CompactUpdateVoteState(vote_state_update)
294 | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => &vote_state_update.hash,
295 Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => &tower_sync.hash,
296 _ => panic!("Tried to get hash on non simple vote instruction"),
297 };
298 Hash::new_from_array(hash.to_bytes())
299 }
300 pub fn timestamp(&self) -> Option<UnixTimestamp> {
302 assert!(self.is_simple_vote());
303 match self {
304 Self::Vote(v) | Self::VoteSwitch(v, _) => v.timestamp,
305 Self::UpdateVoteState(vote_state_update)
306 | Self::UpdateVoteStateSwitch(vote_state_update, _)
307 | Self::CompactUpdateVoteState(vote_state_update)
308 | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
309 vote_state_update.timestamp
310 }
311 Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => {
312 tower_sync.timestamp
313 }
314 _ => panic!("Tried to get timestamp on non simple vote instruction"),
315 }
316 }
317}
318
319#[cfg(feature = "bincode")]
320fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction {
321 let account_metas = vec![
322 AccountMeta::new(*vote_pubkey, false),
323 AccountMeta::new_readonly(sysvar::rent::id(), false),
324 AccountMeta::new_readonly(sysvar::clock::id(), false),
325 AccountMeta::new_readonly(vote_init.node_pubkey, true),
326 ];
327
328 Instruction::new_with_bincode(
329 id(),
330 &VoteInstruction::InitializeAccount(*vote_init),
331 account_metas,
332 )
333}
334
335#[cfg(feature = "bincode")]
336fn initialize_account_v2(
337 vote_pubkey: &Pubkey,
338 vote_init: &VoteInitV2,
339 inflation_rewards_collector: &Pubkey,
340 block_revenue_collector: &Pubkey,
341) -> Instruction {
342 let account_metas = vec![
343 AccountMeta::new(*vote_pubkey, false),
344 AccountMeta::new_readonly(vote_init.node_pubkey, true),
345 AccountMeta::new(*inflation_rewards_collector, false),
346 AccountMeta::new(*block_revenue_collector, false),
347 ];
348
349 Instruction::new_with_bincode(
350 id(),
351 &VoteInstruction::InitializeAccountV2(*vote_init),
352 account_metas,
353 )
354}
355
356pub struct CreateVoteAccountConfig<'a> {
357 pub space: u64,
358 pub with_seed: Option<(&'a Pubkey, &'a str)>,
359}
360
361impl Default for CreateVoteAccountConfig<'_> {
362 fn default() -> Self {
363 Self {
364 space: VoteStateV4::size_of() as u64,
366 with_seed: None,
367 }
368 }
369}
370
371#[cfg(feature = "bincode")]
372pub fn create_account_with_config(
373 from_pubkey: &Pubkey,
374 vote_pubkey: &Pubkey,
375 vote_init: &VoteInit,
376 lamports: u64,
377 config: CreateVoteAccountConfig,
378) -> Vec<Instruction> {
379 let create_ix = if let Some((base, seed)) = config.with_seed {
380 solana_system_interface::instruction::create_account_with_seed(
381 from_pubkey,
382 vote_pubkey,
383 base,
384 seed,
385 lamports,
386 config.space,
387 &id(),
388 )
389 } else {
390 solana_system_interface::instruction::create_account(
391 from_pubkey,
392 vote_pubkey,
393 lamports,
394 config.space,
395 &id(),
396 )
397 };
398 let init_ix = initialize_account(vote_pubkey, vote_init);
399 vec![create_ix, init_ix]
400}
401
402#[cfg(feature = "bincode")]
403pub fn create_account_with_config_v2(
404 from_pubkey: &Pubkey,
405 vote_pubkey: &Pubkey,
406 vote_init: &VoteInitV2,
407 inflation_rewards_collector: &Pubkey,
408 block_revenue_collector: &Pubkey,
409 lamports: u64,
410 config: CreateVoteAccountConfig,
411) -> Vec<Instruction> {
412 let create_ix = if let Some((base, seed)) = config.with_seed {
413 solana_system_interface::instruction::create_account_with_seed(
414 from_pubkey,
415 vote_pubkey,
416 base,
417 seed,
418 lamports,
419 config.space,
420 &id(),
421 )
422 } else {
423 solana_system_interface::instruction::create_account(
424 from_pubkey,
425 vote_pubkey,
426 lamports,
427 config.space,
428 &id(),
429 )
430 };
431 let init_ix = initialize_account_v2(
432 vote_pubkey,
433 vote_init,
434 inflation_rewards_collector,
435 block_revenue_collector,
436 );
437 vec![create_ix, init_ix]
438}
439
440#[cfg(feature = "bincode")]
441pub fn authorize(
442 vote_pubkey: &Pubkey,
443 authorized_pubkey: &Pubkey, new_authorized_pubkey: &Pubkey,
445 vote_authorize: VoteAuthorize,
446) -> Instruction {
447 let account_metas = vec![
448 AccountMeta::new(*vote_pubkey, false),
449 AccountMeta::new_readonly(sysvar::clock::id(), false),
450 AccountMeta::new_readonly(*authorized_pubkey, true),
451 ];
452
453 Instruction::new_with_bincode(
454 id(),
455 &VoteInstruction::Authorize(*new_authorized_pubkey, vote_authorize),
456 account_metas,
457 )
458}
459
460#[cfg(feature = "bincode")]
461pub fn authorize_checked(
462 vote_pubkey: &Pubkey,
463 authorized_pubkey: &Pubkey, new_authorized_pubkey: &Pubkey,
465 vote_authorize: VoteAuthorize,
466) -> Instruction {
467 let account_metas = vec![
468 AccountMeta::new(*vote_pubkey, false),
469 AccountMeta::new_readonly(sysvar::clock::id(), false),
470 AccountMeta::new_readonly(*authorized_pubkey, true),
471 AccountMeta::new_readonly(*new_authorized_pubkey, true),
472 ];
473
474 Instruction::new_with_bincode(
475 id(),
476 &VoteInstruction::AuthorizeChecked(vote_authorize),
477 account_metas,
478 )
479}
480
481#[cfg(feature = "bincode")]
482pub fn authorize_with_seed(
483 vote_pubkey: &Pubkey,
484 current_authority_base_key: &Pubkey,
485 current_authority_derived_key_owner: &Pubkey,
486 current_authority_derived_key_seed: &str,
487 new_authority: &Pubkey,
488 authorization_type: VoteAuthorize,
489) -> Instruction {
490 let account_metas = vec![
491 AccountMeta::new(*vote_pubkey, false),
492 AccountMeta::new_readonly(sysvar::clock::id(), false),
493 AccountMeta::new_readonly(*current_authority_base_key, true),
494 ];
495
496 Instruction::new_with_bincode(
497 id(),
498 &VoteInstruction::AuthorizeWithSeed(VoteAuthorizeWithSeedArgs {
499 authorization_type,
500 current_authority_derived_key_owner: *current_authority_derived_key_owner,
501 current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
502 new_authority: *new_authority,
503 }),
504 account_metas,
505 )
506}
507
508#[cfg(feature = "bincode")]
509pub fn authorize_checked_with_seed(
510 vote_pubkey: &Pubkey,
511 current_authority_base_key: &Pubkey,
512 current_authority_derived_key_owner: &Pubkey,
513 current_authority_derived_key_seed: &str,
514 new_authority: &Pubkey,
515 authorization_type: VoteAuthorize,
516) -> Instruction {
517 let account_metas = vec![
518 AccountMeta::new(*vote_pubkey, false),
519 AccountMeta::new_readonly(sysvar::clock::id(), false),
520 AccountMeta::new_readonly(*current_authority_base_key, true),
521 AccountMeta::new_readonly(*new_authority, true),
522 ];
523
524 Instruction::new_with_bincode(
525 id(),
526 &VoteInstruction::AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs {
527 authorization_type,
528 current_authority_derived_key_owner: *current_authority_derived_key_owner,
529 current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
530 }),
531 account_metas,
532 )
533}
534
535#[cfg(feature = "bincode")]
536pub fn update_validator_identity(
537 vote_pubkey: &Pubkey,
538 authorized_withdrawer_pubkey: &Pubkey,
539 node_pubkey: &Pubkey,
540) -> Instruction {
541 let account_metas = vec![
542 AccountMeta::new(*vote_pubkey, false),
543 AccountMeta::new_readonly(*node_pubkey, true),
544 AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
545 ];
546
547 Instruction::new_with_bincode(
548 id(),
549 &VoteInstruction::UpdateValidatorIdentity,
550 account_metas,
551 )
552}
553
554#[cfg(feature = "bincode")]
555pub fn update_commission(
556 vote_pubkey: &Pubkey,
557 authorized_withdrawer_pubkey: &Pubkey,
558 commission: u8,
559) -> Instruction {
560 let account_metas = vec![
561 AccountMeta::new(*vote_pubkey, false),
562 AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
563 ];
564
565 Instruction::new_with_bincode(
566 id(),
567 &VoteInstruction::UpdateCommission(commission),
568 account_metas,
569 )
570}
571
572#[cfg(feature = "bincode")]
573pub fn update_commission_collector(
574 vote_pubkey: &Pubkey,
575 authorized_withdrawer_pubkey: &Pubkey,
576 new_collector_pubkey: &Pubkey,
577 kind: CommissionKind,
578) -> Instruction {
579 let account_metas = vec![
580 AccountMeta::new(*vote_pubkey, false),
581 AccountMeta::new(*new_collector_pubkey, false),
582 AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
583 ];
584
585 Instruction::new_with_bincode(
586 id(),
587 &VoteInstruction::UpdateCommissionCollector(kind),
588 account_metas,
589 )
590}
591
592#[cfg(feature = "bincode")]
593pub fn update_commission_bps(
594 vote_pubkey: &Pubkey,
595 authorized_withdrawer_pubkey: &Pubkey,
596 kind: CommissionKind,
597 commission_bps: u16,
598) -> Instruction {
599 let account_metas = vec![
600 AccountMeta::new(*vote_pubkey, false),
601 AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
602 ];
603
604 Instruction::new_with_bincode(
605 id(),
606 &VoteInstruction::UpdateCommissionBps {
607 kind,
608 commission_bps,
609 },
610 account_metas,
611 )
612}
613
614#[cfg(feature = "bincode")]
615pub fn deposit_delegator_rewards(
616 vote_pubkey: &Pubkey,
617 source_pubkey: &Pubkey,
618 deposit: u64,
619) -> Instruction {
620 let account_metas = vec![
621 AccountMeta::new(*vote_pubkey, false),
622 AccountMeta::new(*source_pubkey, true),
623 ];
624
625 Instruction::new_with_bincode(
626 id(),
627 &VoteInstruction::DepositDelegatorRewards { deposit },
628 account_metas,
629 )
630}
631
632#[cfg(feature = "bincode")]
633pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction {
634 let account_metas = vec![
635 AccountMeta::new(*vote_pubkey, false),
636 AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
637 AccountMeta::new_readonly(sysvar::clock::id(), false),
638 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
639 ];
640
641 Instruction::new_with_bincode(id(), &VoteInstruction::Vote(vote), account_metas)
642}
643
644#[cfg(feature = "bincode")]
645pub fn vote_switch(
646 vote_pubkey: &Pubkey,
647 authorized_voter_pubkey: &Pubkey,
648 vote: Vote,
649 proof_hash: Hash,
650) -> Instruction {
651 let account_metas = vec![
652 AccountMeta::new(*vote_pubkey, false),
653 AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
654 AccountMeta::new_readonly(sysvar::clock::id(), false),
655 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
656 ];
657
658 Instruction::new_with_bincode(
659 id(),
660 &VoteInstruction::VoteSwitch(vote, proof_hash),
661 account_metas,
662 )
663}
664
665#[cfg(feature = "bincode")]
666pub fn update_vote_state(
667 vote_pubkey: &Pubkey,
668 authorized_voter_pubkey: &Pubkey,
669 vote_state_update: VoteStateUpdate,
670) -> Instruction {
671 let account_metas = vec![
672 AccountMeta::new(*vote_pubkey, false),
673 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
674 ];
675
676 Instruction::new_with_bincode(
677 id(),
678 &VoteInstruction::UpdateVoteState(vote_state_update),
679 account_metas,
680 )
681}
682
683#[cfg(feature = "bincode")]
684pub fn update_vote_state_switch(
685 vote_pubkey: &Pubkey,
686 authorized_voter_pubkey: &Pubkey,
687 vote_state_update: VoteStateUpdate,
688 proof_hash: Hash,
689) -> Instruction {
690 let account_metas = vec![
691 AccountMeta::new(*vote_pubkey, false),
692 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
693 ];
694
695 Instruction::new_with_bincode(
696 id(),
697 &VoteInstruction::UpdateVoteStateSwitch(vote_state_update, proof_hash),
698 account_metas,
699 )
700}
701
702#[cfg(feature = "bincode")]
703pub fn compact_update_vote_state(
704 vote_pubkey: &Pubkey,
705 authorized_voter_pubkey: &Pubkey,
706 vote_state_update: VoteStateUpdate,
707) -> Instruction {
708 let account_metas = vec![
709 AccountMeta::new(*vote_pubkey, false),
710 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
711 ];
712
713 Instruction::new_with_bincode(
714 id(),
715 &VoteInstruction::CompactUpdateVoteState(vote_state_update),
716 account_metas,
717 )
718}
719
720#[cfg(feature = "bincode")]
721pub fn compact_update_vote_state_switch(
722 vote_pubkey: &Pubkey,
723 authorized_voter_pubkey: &Pubkey,
724 vote_state_update: VoteStateUpdate,
725 proof_hash: Hash,
726) -> Instruction {
727 let account_metas = vec![
728 AccountMeta::new(*vote_pubkey, false),
729 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
730 ];
731
732 Instruction::new_with_bincode(
733 id(),
734 &VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, proof_hash),
735 account_metas,
736 )
737}
738
739#[cfg(feature = "bincode")]
740pub fn tower_sync(
741 vote_pubkey: &Pubkey,
742 authorized_voter_pubkey: &Pubkey,
743 tower_sync: TowerSync,
744) -> Instruction {
745 let account_metas = vec![
746 AccountMeta::new(*vote_pubkey, false),
747 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
748 ];
749
750 Instruction::new_with_bincode(id(), &VoteInstruction::TowerSync(tower_sync), account_metas)
751}
752
753#[cfg(feature = "bincode")]
754pub fn tower_sync_switch(
755 vote_pubkey: &Pubkey,
756 authorized_voter_pubkey: &Pubkey,
757 tower_sync: TowerSync,
758 proof_hash: Hash,
759) -> Instruction {
760 let account_metas = vec![
761 AccountMeta::new(*vote_pubkey, false),
762 AccountMeta::new_readonly(*authorized_voter_pubkey, true),
763 ];
764
765 Instruction::new_with_bincode(
766 id(),
767 &VoteInstruction::TowerSyncSwitch(tower_sync, proof_hash),
768 account_metas,
769 )
770}
771
772#[cfg(feature = "bincode")]
773pub fn withdraw(
774 vote_pubkey: &Pubkey,
775 authorized_withdrawer_pubkey: &Pubkey,
776 lamports: u64,
777 to_pubkey: &Pubkey,
778) -> Instruction {
779 let account_metas = vec![
780 AccountMeta::new(*vote_pubkey, false),
781 AccountMeta::new(*to_pubkey, false),
782 AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
783 ];
784
785 Instruction::new_with_bincode(id(), &VoteInstruction::Withdraw(lamports), account_metas)
786}