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