Skip to main content

solana_vote_interface/
instruction.rs

1//! Vote program instructions
2
3#[cfg(feature = "stable-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(any(feature = "bincode", feature = "wincode"))]
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 = "stable-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 = "stable-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    /// Initialize a vote account
55    ///
56    /// # Account references
57    ///   0. `[WRITE]` Uninitialized vote account
58    ///   1. `[]` Rent sysvar
59    ///   2. `[]` Clock sysvar
60    ///   3. `[SIGNER]` New validator identity (node_pubkey)
61    ///
62    /// This instruction MUST be included within the same Transaction as the
63    /// system program's `CreateAccount` instruction that creates the account
64    /// being initialized. Otherwise another party can acquire ownership of the
65    /// uninitialized account.
66    InitializeAccount(VoteInit),
67
68    /// Authorize a key to send votes or issue a withdrawal
69    ///
70    /// # Account references
71    ///   0. `[WRITE]` Vote account to be updated with the Pubkey for authorization
72    ///   1. `[]` Clock sysvar
73    ///   2. `[SIGNER]` Vote or withdraw authority
74    ///
75    /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant is
76    /// disallowed for any vote accounts whose BLS pubkey is set to `Some`.
77    Authorize(Pubkey, VoteAuthorize),
78
79    /// A Vote instruction with recent votes
80    ///
81    /// # Account references
82    ///   0. `[WRITE]` Vote account to vote with
83    ///   1. `[]` Slot hashes sysvar
84    ///   2. `[]` Clock sysvar
85    ///   3. `[SIGNER]` Vote authority
86    Vote(Vote),
87
88    /// Withdraw some amount of funds
89    ///
90    /// # Account references
91    ///   0. `[WRITE]` Vote account to withdraw from
92    ///   1. `[WRITE]` Recipient account
93    ///   2. `[SIGNER]` Withdraw authority
94    Withdraw(u64),
95
96    /// Update the vote account's validator identity (node_pubkey)
97    ///
98    /// # Account references
99    ///   0. `[WRITE]` Vote account to be updated with the given authority public key
100    ///   1. `[SIGNER]` New validator identity (node_pubkey)
101    ///   2. `[SIGNER]` Withdraw authority
102    UpdateValidatorIdentity,
103
104    /// Update the commission for the vote account
105    ///
106    /// # Account references
107    ///   0. `[WRITE]` Vote account to be updated
108    ///   1. `[SIGNER]` Withdraw authority
109    UpdateCommission(u8),
110
111    /// A Vote instruction with recent votes
112    ///
113    /// # Account references
114    ///   0. `[WRITE]` Vote account to vote with
115    ///   1. `[]` Slot hashes sysvar
116    ///   2. `[]` Clock sysvar
117    ///   3. `[SIGNER]` Vote authority
118    VoteSwitch(Vote, Hash),
119
120    /// Authorize a key to send votes or issue a withdrawal
121    ///
122    /// This instruction behaves like `Authorize` with the additional requirement that the new vote
123    /// or withdraw authority must also be a signer.
124    ///
125    /// # Account references
126    ///   0. `[WRITE]` Vote account to be updated with the Pubkey for authorization
127    ///   1. `[]` Clock sysvar
128    ///   2. `[SIGNER]` Vote or withdraw authority
129    ///   3. `[SIGNER]` New vote or withdraw authority
130    ///
131    /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant is
132    /// disallowed for any vote accounts whose BLS pubkey is set to `Some`.
133    AuthorizeChecked(VoteAuthorize),
134
135    /// Update the onchain vote state for the signer.
136    ///
137    /// # Account references
138    ///   0. `[Write]` Vote account to vote with
139    ///   1. `[SIGNER]` Vote authority
140    UpdateVoteState(VoteStateUpdate),
141
142    /// Update the onchain vote state for the signer along with a switching proof.
143    ///
144    /// # Account references
145    ///   0. `[Write]` Vote account to vote with
146    ///   1. `[SIGNER]` Vote authority
147    UpdateVoteStateSwitch(VoteStateUpdate, Hash),
148
149    /// Given that the current Voter or Withdrawer authority is a derived key,
150    /// this instruction allows someone who can sign for that derived key's
151    /// base key to authorize a new Voter or Withdrawer for a vote account.
152    ///
153    /// # Account references
154    ///   0. `[Write]` Vote account to be updated
155    ///   1. `[]` Clock sysvar
156    ///   2. `[SIGNER]` Base key of current Voter or Withdrawer authority's derived key
157    ///
158    /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant in
159    /// `authorization_type` is disallowed for any vote accounts whose BLS
160    /// pubkey is set to `Some`.
161    AuthorizeWithSeed(VoteAuthorizeWithSeedArgs),
162
163    /// Given that the current Voter or Withdrawer authority is a derived key,
164    /// this instruction allows someone who can sign for that derived key's
165    /// base key to authorize a new Voter or Withdrawer for a vote account.
166    ///
167    /// This instruction behaves like `AuthorizeWithSeed` with the additional requirement
168    /// that the new vote or withdraw authority must also be a signer.
169    ///
170    /// # Account references
171    ///   0. `[Write]` Vote account to be updated
172    ///   1. `[]` Clock sysvar
173    ///   2. `[SIGNER]` Base key of current Voter or Withdrawer authority's derived key
174    ///   3. `[SIGNER]` New vote or withdraw authority
175    ///
176    /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant in
177    /// `authorization_type` is disallowed for any vote accounts whose BLS
178    /// pubkey is set to `Some`.
179    AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs),
180
181    /// Update the onchain vote state for the signer.
182    ///
183    /// # Account references
184    ///   0. `[Write]` Vote account to vote with
185    ///   1. `[SIGNER]` Vote authority
186    #[cfg_attr(feature = "serde", serde(with = "serde_compact_vote_state_update"))]
187    CompactUpdateVoteState(
188        #[cfg_attr(feature = "wincode", wincode(with = "CompactVoteStateUpdate"))] VoteStateUpdate,
189    ),
190
191    /// Update the onchain vote state for the signer along with a switching proof.
192    ///
193    /// # Account references
194    ///   0. `[Write]` Vote account to vote with
195    ///   1. `[SIGNER]` Vote authority
196    CompactUpdateVoteStateSwitch(
197        #[cfg_attr(feature = "serde", serde(with = "serde_compact_vote_state_update"))]
198        #[cfg_attr(feature = "wincode", wincode(with = "CompactVoteStateUpdate"))]
199        VoteStateUpdate,
200        Hash,
201    ),
202
203    /// Sync the onchain vote state with local tower
204    ///
205    /// # Account references
206    ///   0. `[Write]` Vote account to vote with
207    ///   1. `[SIGNER]` Vote authority
208    #[cfg_attr(feature = "serde", serde(with = "serde_tower_sync"))]
209    TowerSync(#[cfg_attr(feature = "wincode", wincode(with = "CompactTowerSync"))] TowerSync),
210
211    /// Sync the onchain vote state with local tower along with a switching proof
212    ///
213    /// # Account references
214    ///   0. `[Write]` Vote account to vote with
215    ///   1. `[SIGNER]` Vote authority
216    TowerSyncSwitch(
217        #[cfg_attr(feature = "serde", serde(with = "serde_tower_sync"))]
218        #[cfg_attr(feature = "wincode", wincode(with = "CompactTowerSync"))]
219        TowerSync,
220        Hash,
221    ),
222
223    /// Initialize a vote account with all vote state v4 fields, including
224    /// BLS public key and collector accounts.
225    ///
226    /// The vote program performs BLS proof-of-possession verification on the
227    /// submitted BLS public key (per SIMD-0387). For each collector account
228    /// (indices 2 and 3), the program performs the same validation as
229    /// `UpdateCommissionCollector` (per SIMD-0232): if the collector is not
230    /// the vote account itself, it must be system-program owned, rent-exempt,
231    /// and writable (not a reserved account).
232    ///
233    /// # Account references
234    ///   0. `[WRITE]` Uninitialized vote account
235    ///   1. `[SIGNER]` New validator identity (node_pubkey)
236    ///   2. `[WRITE]` Inflation rewards collector
237    ///   3. `[WRITE]` Block revenue collector
238    ///
239    /// This instruction MUST be included within the same Transaction as the
240    /// system program's `CreateAccount` instruction that creates the account
241    /// being initialized. Otherwise another party can acquire ownership of the
242    /// uninitialized account.
243    InitializeAccountV2(VoteInitV2),
244
245    /// Update the commission collector for the vote account
246    ///
247    /// # Account references
248    ///   0. `[WRITE]` Vote account to be updated with the new collector public key
249    ///   1. `[WRITE]` New collector account. Must be set to the vote account or
250    ///      a system program owned account. Must be writable to ensure the
251    ///      account is not reserved.
252    ///   2. `[SIGNER]` Vote account withdraw authority
253    UpdateCommissionCollector(CommissionKind),
254
255    /// Update the commission rate in basis points for the specified commission
256    /// rate kind in a vote account.
257    ///
258    /// # Account references
259    ///   0. `[WRITE]` Vote account to be updated with the new commission
260    ///   1. `[SIGNER]` Vote account withdraw authority
261    UpdateCommissionBps {
262        commission_bps: u16,
263        kind: CommissionKind,
264    },
265
266    /// Deposit lamports for distribution to stake delegators
267    ///
268    /// # Account references
269    ///   0. `[WRITE]` Vote account to be updated with the deposit
270    ///   1. `[SIGNER, WRITE]` Source account for deposit funds
271    ///   2. `[]` System program, used to transfer the deposit
272    DepositDelegatorRewards { deposit: u64 },
273}
274
275impl VoteInstruction {
276    pub fn is_simple_vote(&self) -> bool {
277        matches!(
278            self,
279            Self::Vote(_)
280                | Self::VoteSwitch(_, _)
281                | Self::UpdateVoteState(_)
282                | Self::UpdateVoteStateSwitch(_, _)
283                | Self::CompactUpdateVoteState(_)
284                | Self::CompactUpdateVoteStateSwitch(_, _)
285                | Self::TowerSync(_)
286                | Self::TowerSyncSwitch(_, _),
287        )
288    }
289
290    pub fn is_single_vote_state_update(&self) -> bool {
291        matches!(
292            self,
293            Self::UpdateVoteState(_)
294                | Self::UpdateVoteStateSwitch(_, _)
295                | Self::CompactUpdateVoteState(_)
296                | Self::CompactUpdateVoteStateSwitch(_, _)
297                | Self::TowerSync(_)
298                | Self::TowerSyncSwitch(_, _),
299        )
300    }
301
302    /// Only to be used on vote instructions (guard with is_simple_vote),  panics otherwise
303    pub fn last_voted_slot(&self) -> Option<Slot> {
304        assert!(self.is_simple_vote());
305        match self {
306            Self::Vote(v) | Self::VoteSwitch(v, _) => v.last_voted_slot(),
307            Self::UpdateVoteState(vote_state_update)
308            | Self::UpdateVoteStateSwitch(vote_state_update, _)
309            | Self::CompactUpdateVoteState(vote_state_update)
310            | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
311                vote_state_update.last_voted_slot()
312            }
313            Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => {
314                tower_sync.last_voted_slot()
315            }
316            _ => panic!("Tried to get slot on non simple vote instruction"),
317        }
318    }
319
320    /// Only to be used on vote instructions (guard with is_simple_vote), panics otherwise
321    pub fn hash(&self) -> Hash {
322        assert!(self.is_simple_vote());
323        let hash = match self {
324            Self::Vote(v) | Self::VoteSwitch(v, _) => &v.hash,
325            Self::UpdateVoteState(vote_state_update)
326            | Self::UpdateVoteStateSwitch(vote_state_update, _)
327            | Self::CompactUpdateVoteState(vote_state_update)
328            | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => &vote_state_update.hash,
329            Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => &tower_sync.hash,
330            _ => panic!("Tried to get hash on non simple vote instruction"),
331        };
332        Hash::new_from_array(hash.to_bytes())
333    }
334    /// Only to be used on vote instructions (guard with is_simple_vote),  panics otherwise
335    pub fn timestamp(&self) -> Option<UnixTimestamp> {
336        assert!(self.is_simple_vote());
337        match self {
338            Self::Vote(v) | Self::VoteSwitch(v, _) => v.timestamp,
339            Self::UpdateVoteState(vote_state_update)
340            | Self::UpdateVoteStateSwitch(vote_state_update, _)
341            | Self::CompactUpdateVoteState(vote_state_update)
342            | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
343                vote_state_update.timestamp
344            }
345            Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => {
346                tower_sync.timestamp
347            }
348            _ => panic!("Tried to get timestamp on non simple vote instruction"),
349        }
350    }
351}
352
353#[cfg(all(feature = "bincode", not(feature = "wincode")))]
354#[inline(always)]
355fn create_instruction(
356    program_id: Pubkey,
357    data: &VoteInstruction,
358    accounts: Vec<AccountMeta>,
359) -> Instruction {
360    Instruction::new_with_bincode(program_id, data, accounts)
361}
362
363#[cfg(feature = "wincode")]
364#[inline(always)]
365fn create_instruction(
366    program_id: Pubkey,
367    data: &VoteInstruction,
368    accounts: Vec<AccountMeta>,
369) -> Instruction {
370    Instruction::new_with_wincode(program_id, data, accounts)
371}
372
373#[cfg(any(feature = "bincode", feature = "wincode"))]
374fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction {
375    let account_metas = vec![
376        AccountMeta::new(*vote_pubkey, false),
377        AccountMeta::new_readonly(sysvar::rent::id(), false),
378        AccountMeta::new_readonly(sysvar::clock::id(), false),
379        AccountMeta::new_readonly(vote_init.node_pubkey, true),
380    ];
381
382    create_instruction(
383        id(),
384        &VoteInstruction::InitializeAccount(*vote_init),
385        account_metas,
386    )
387}
388
389#[cfg(any(feature = "bincode", feature = "wincode"))]
390fn initialize_account_v2(
391    vote_pubkey: &Pubkey,
392    vote_init: &VoteInitV2,
393    inflation_rewards_collector: &Pubkey,
394    block_revenue_collector: &Pubkey,
395) -> Instruction {
396    let account_metas = vec![
397        AccountMeta::new(*vote_pubkey, false),
398        AccountMeta::new_readonly(vote_init.node_pubkey, true),
399        AccountMeta::new(*inflation_rewards_collector, false),
400        AccountMeta::new(*block_revenue_collector, false),
401    ];
402
403    create_instruction(
404        id(),
405        &VoteInstruction::InitializeAccountV2(*vote_init),
406        account_metas,
407    )
408}
409
410pub struct CreateVoteAccountConfig<'a> {
411    pub space: u64,
412    pub with_seed: Option<(&'a Pubkey, &'a str)>,
413}
414
415impl Default for CreateVoteAccountConfig<'_> {
416    fn default() -> Self {
417        Self {
418            // Create new vote accounts with size for V4.
419            space: VoteStateV4::size_of() as u64,
420            with_seed: None,
421        }
422    }
423}
424
425#[cfg(any(feature = "bincode", feature = "wincode"))]
426pub fn create_account_with_config(
427    from_pubkey: &Pubkey,
428    vote_pubkey: &Pubkey,
429    vote_init: &VoteInit,
430    lamports: u64,
431    config: CreateVoteAccountConfig,
432) -> Vec<Instruction> {
433    let create_ix = if let Some((base, seed)) = config.with_seed {
434        solana_system_interface::instruction::create_account_with_seed(
435            from_pubkey,
436            vote_pubkey,
437            base,
438            seed,
439            lamports,
440            config.space,
441            &id(),
442        )
443    } else {
444        solana_system_interface::instruction::create_account(
445            from_pubkey,
446            vote_pubkey,
447            lamports,
448            config.space,
449            &id(),
450        )
451    };
452    let init_ix = initialize_account(vote_pubkey, vote_init);
453    vec![create_ix, init_ix]
454}
455
456#[cfg(any(feature = "bincode", feature = "wincode"))]
457pub fn create_account_with_config_v2(
458    from_pubkey: &Pubkey,
459    vote_pubkey: &Pubkey,
460    vote_init: &VoteInitV2,
461    inflation_rewards_collector: &Pubkey,
462    block_revenue_collector: &Pubkey,
463    lamports: u64,
464    config: CreateVoteAccountConfig,
465) -> Vec<Instruction> {
466    let create_ix = if let Some((base, seed)) = config.with_seed {
467        solana_system_interface::instruction::create_account_with_seed(
468            from_pubkey,
469            vote_pubkey,
470            base,
471            seed,
472            lamports,
473            config.space,
474            &id(),
475        )
476    } else {
477        solana_system_interface::instruction::create_account(
478            from_pubkey,
479            vote_pubkey,
480            lamports,
481            config.space,
482            &id(),
483        )
484    };
485    let init_ix = initialize_account_v2(
486        vote_pubkey,
487        vote_init,
488        inflation_rewards_collector,
489        block_revenue_collector,
490    );
491    vec![create_ix, init_ix]
492}
493
494#[cfg(any(feature = "bincode", feature = "wincode"))]
495pub fn authorize(
496    vote_pubkey: &Pubkey,
497    authorized_pubkey: &Pubkey, // currently authorized
498    new_authorized_pubkey: &Pubkey,
499    vote_authorize: VoteAuthorize,
500) -> Instruction {
501    let account_metas = vec![
502        AccountMeta::new(*vote_pubkey, false),
503        AccountMeta::new_readonly(sysvar::clock::id(), false),
504        AccountMeta::new_readonly(*authorized_pubkey, true),
505    ];
506
507    create_instruction(
508        id(),
509        &VoteInstruction::Authorize(*new_authorized_pubkey, vote_authorize),
510        account_metas,
511    )
512}
513
514#[cfg(any(feature = "bincode", feature = "wincode"))]
515pub fn authorize_checked(
516    vote_pubkey: &Pubkey,
517    authorized_pubkey: &Pubkey, // currently authorized
518    new_authorized_pubkey: &Pubkey,
519    vote_authorize: VoteAuthorize,
520) -> Instruction {
521    let account_metas = vec![
522        AccountMeta::new(*vote_pubkey, false),
523        AccountMeta::new_readonly(sysvar::clock::id(), false),
524        AccountMeta::new_readonly(*authorized_pubkey, true),
525        AccountMeta::new_readonly(*new_authorized_pubkey, true),
526    ];
527
528    create_instruction(
529        id(),
530        &VoteInstruction::AuthorizeChecked(vote_authorize),
531        account_metas,
532    )
533}
534
535#[cfg(any(feature = "bincode", feature = "wincode"))]
536pub fn authorize_with_seed(
537    vote_pubkey: &Pubkey,
538    current_authority_base_key: &Pubkey,
539    current_authority_derived_key_owner: &Pubkey,
540    current_authority_derived_key_seed: &str,
541    new_authority: &Pubkey,
542    authorization_type: VoteAuthorize,
543) -> Instruction {
544    let account_metas = vec![
545        AccountMeta::new(*vote_pubkey, false),
546        AccountMeta::new_readonly(sysvar::clock::id(), false),
547        AccountMeta::new_readonly(*current_authority_base_key, true),
548    ];
549
550    create_instruction(
551        id(),
552        &VoteInstruction::AuthorizeWithSeed(VoteAuthorizeWithSeedArgs {
553            authorization_type,
554            current_authority_derived_key_owner: *current_authority_derived_key_owner,
555            current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
556            new_authority: *new_authority,
557        }),
558        account_metas,
559    )
560}
561
562#[cfg(any(feature = "bincode", feature = "wincode"))]
563pub fn authorize_checked_with_seed(
564    vote_pubkey: &Pubkey,
565    current_authority_base_key: &Pubkey,
566    current_authority_derived_key_owner: &Pubkey,
567    current_authority_derived_key_seed: &str,
568    new_authority: &Pubkey,
569    authorization_type: VoteAuthorize,
570) -> Instruction {
571    let account_metas = vec![
572        AccountMeta::new(*vote_pubkey, false),
573        AccountMeta::new_readonly(sysvar::clock::id(), false),
574        AccountMeta::new_readonly(*current_authority_base_key, true),
575        AccountMeta::new_readonly(*new_authority, true),
576    ];
577
578    create_instruction(
579        id(),
580        &VoteInstruction::AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs {
581            authorization_type,
582            current_authority_derived_key_owner: *current_authority_derived_key_owner,
583            current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
584        }),
585        account_metas,
586    )
587}
588
589#[cfg(any(feature = "bincode", feature = "wincode"))]
590pub fn update_validator_identity(
591    vote_pubkey: &Pubkey,
592    authorized_withdrawer_pubkey: &Pubkey,
593    node_pubkey: &Pubkey,
594) -> Instruction {
595    let account_metas = vec![
596        AccountMeta::new(*vote_pubkey, false),
597        AccountMeta::new_readonly(*node_pubkey, true),
598        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
599    ];
600
601    create_instruction(
602        id(),
603        &VoteInstruction::UpdateValidatorIdentity,
604        account_metas,
605    )
606}
607
608#[cfg(any(feature = "bincode", feature = "wincode"))]
609pub fn update_commission(
610    vote_pubkey: &Pubkey,
611    authorized_withdrawer_pubkey: &Pubkey,
612    commission: u8,
613) -> Instruction {
614    let account_metas = vec![
615        AccountMeta::new(*vote_pubkey, false),
616        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
617    ];
618
619    create_instruction(
620        id(),
621        &VoteInstruction::UpdateCommission(commission),
622        account_metas,
623    )
624}
625
626#[cfg(any(feature = "bincode", feature = "wincode"))]
627pub fn update_commission_collector(
628    vote_pubkey: &Pubkey,
629    authorized_withdrawer_pubkey: &Pubkey,
630    new_collector_pubkey: &Pubkey,
631    kind: CommissionKind,
632) -> Instruction {
633    let account_metas = vec![
634        AccountMeta::new(*vote_pubkey, false),
635        AccountMeta::new(*new_collector_pubkey, false),
636        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
637    ];
638
639    create_instruction(
640        id(),
641        &VoteInstruction::UpdateCommissionCollector(kind),
642        account_metas,
643    )
644}
645
646#[cfg(any(feature = "bincode", feature = "wincode"))]
647pub fn update_commission_bps(
648    vote_pubkey: &Pubkey,
649    authorized_withdrawer_pubkey: &Pubkey,
650    kind: CommissionKind,
651    commission_bps: u16,
652) -> Instruction {
653    let account_metas = vec![
654        AccountMeta::new(*vote_pubkey, false),
655        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
656    ];
657
658    create_instruction(
659        id(),
660        &VoteInstruction::UpdateCommissionBps {
661            kind,
662            commission_bps,
663        },
664        account_metas,
665    )
666}
667
668#[cfg(any(feature = "bincode", feature = "wincode"))]
669pub fn deposit_delegator_rewards(
670    vote_pubkey: &Pubkey,
671    source_pubkey: &Pubkey,
672    deposit: u64,
673) -> Instruction {
674    let account_metas = vec![
675        AccountMeta::new(*vote_pubkey, false),
676        AccountMeta::new(*source_pubkey, true),
677        AccountMeta::new_readonly(system_program::id(), false),
678    ];
679
680    create_instruction(
681        id(),
682        &VoteInstruction::DepositDelegatorRewards { deposit },
683        account_metas,
684    )
685}
686
687#[cfg(any(feature = "bincode", feature = "wincode"))]
688pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction {
689    let account_metas = vec![
690        AccountMeta::new(*vote_pubkey, false),
691        AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
692        AccountMeta::new_readonly(sysvar::clock::id(), false),
693        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
694    ];
695
696    create_instruction(id(), &VoteInstruction::Vote(vote), account_metas)
697}
698
699#[cfg(any(feature = "bincode", feature = "wincode"))]
700pub fn vote_switch(
701    vote_pubkey: &Pubkey,
702    authorized_voter_pubkey: &Pubkey,
703    vote: Vote,
704    proof_hash: Hash,
705) -> Instruction {
706    let account_metas = vec![
707        AccountMeta::new(*vote_pubkey, false),
708        AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
709        AccountMeta::new_readonly(sysvar::clock::id(), false),
710        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
711    ];
712
713    create_instruction(
714        id(),
715        &VoteInstruction::VoteSwitch(vote, proof_hash),
716        account_metas,
717    )
718}
719
720#[cfg(any(feature = "bincode", feature = "wincode"))]
721pub fn update_vote_state(
722    vote_pubkey: &Pubkey,
723    authorized_voter_pubkey: &Pubkey,
724    vote_state_update: VoteStateUpdate,
725) -> Instruction {
726    let account_metas = vec![
727        AccountMeta::new(*vote_pubkey, false),
728        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
729    ];
730
731    create_instruction(
732        id(),
733        &VoteInstruction::UpdateVoteState(vote_state_update),
734        account_metas,
735    )
736}
737
738#[cfg(any(feature = "bincode", feature = "wincode"))]
739pub fn update_vote_state_switch(
740    vote_pubkey: &Pubkey,
741    authorized_voter_pubkey: &Pubkey,
742    vote_state_update: VoteStateUpdate,
743    proof_hash: Hash,
744) -> Instruction {
745    let account_metas = vec![
746        AccountMeta::new(*vote_pubkey, false),
747        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
748    ];
749
750    create_instruction(
751        id(),
752        &VoteInstruction::UpdateVoteStateSwitch(vote_state_update, proof_hash),
753        account_metas,
754    )
755}
756
757#[cfg(any(feature = "bincode", feature = "wincode"))]
758pub fn compact_update_vote_state(
759    vote_pubkey: &Pubkey,
760    authorized_voter_pubkey: &Pubkey,
761    vote_state_update: VoteStateUpdate,
762) -> Instruction {
763    let account_metas = vec![
764        AccountMeta::new(*vote_pubkey, false),
765        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
766    ];
767
768    create_instruction(
769        id(),
770        &VoteInstruction::CompactUpdateVoteState(vote_state_update),
771        account_metas,
772    )
773}
774
775#[cfg(any(feature = "bincode", feature = "wincode"))]
776pub fn compact_update_vote_state_switch(
777    vote_pubkey: &Pubkey,
778    authorized_voter_pubkey: &Pubkey,
779    vote_state_update: VoteStateUpdate,
780    proof_hash: Hash,
781) -> Instruction {
782    let account_metas = vec![
783        AccountMeta::new(*vote_pubkey, false),
784        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
785    ];
786
787    create_instruction(
788        id(),
789        &VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, proof_hash),
790        account_metas,
791    )
792}
793
794#[cfg(any(feature = "bincode", feature = "wincode"))]
795pub fn tower_sync(
796    vote_pubkey: &Pubkey,
797    authorized_voter_pubkey: &Pubkey,
798    tower_sync: TowerSync,
799) -> Instruction {
800    let account_metas = vec![
801        AccountMeta::new(*vote_pubkey, false),
802        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
803    ];
804
805    create_instruction(id(), &VoteInstruction::TowerSync(tower_sync), account_metas)
806}
807
808#[cfg(any(feature = "bincode", feature = "wincode"))]
809pub fn tower_sync_switch(
810    vote_pubkey: &Pubkey,
811    authorized_voter_pubkey: &Pubkey,
812    tower_sync: TowerSync,
813    proof_hash: Hash,
814) -> Instruction {
815    let account_metas = vec![
816        AccountMeta::new(*vote_pubkey, false),
817        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
818    ];
819
820    create_instruction(
821        id(),
822        &VoteInstruction::TowerSyncSwitch(tower_sync, proof_hash),
823        account_metas,
824    )
825}
826
827#[cfg(any(feature = "bincode", feature = "wincode"))]
828pub fn withdraw(
829    vote_pubkey: &Pubkey,
830    authorized_withdrawer_pubkey: &Pubkey,
831    lamports: u64,
832    to_pubkey: &Pubkey,
833) -> Instruction {
834    let account_metas = vec![
835        AccountMeta::new(*vote_pubkey, false),
836        AccountMeta::new(*to_pubkey, false),
837        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
838    ];
839
840    create_instruction(id(), &VoteInstruction::Withdraw(lamports), account_metas)
841}
842
843#[cfg(all(test, feature = "bincode"))]
844mod tests {
845    use {super::*, crate::state::Lockout, std::collections::VecDeque};
846
847    #[test]
848    fn test_compact_serialize_rejects_confirmation_count_above_u8() {
849        // The compact wire format stores `confirmation_count` as a `u8`, so a
850        // value that does not fit (> 255) must fail to serialize rather than be
851        // silently truncated.
852        let lockouts = VecDeque::from([Lockout::new_with_confirmation_count(1, 256)]);
853        let vote_state_update = VoteStateUpdate::new(lockouts.clone(), None, Hash::default());
854        let tower_sync = TowerSync::new(lockouts, None, Hash::default(), Hash::default());
855
856        for ix in [
857            VoteInstruction::CompactUpdateVoteState(vote_state_update),
858            VoteInstruction::TowerSync(tower_sync),
859        ] {
860            let err = bincode::serialize(&ix).unwrap_err();
861            assert!(
862                err.to_string().contains("Invalid confirmation count"),
863                "unexpected error: {err}"
864            );
865            #[cfg(feature = "wincode")]
866            assert!(wincode::serialize(&ix).is_err());
867        }
868    }
869
870    #[test]
871    fn test_deposit_delegator_rewards_includes_system_program() {
872        let vote_pubkey = Pubkey::new_unique();
873        let source_pubkey = Pubkey::new_unique();
874
875        let instruction = deposit_delegator_rewards(&vote_pubkey, &source_pubkey, 100_000);
876
877        assert_eq!(instruction.program_id, id());
878        assert_eq!(instruction.accounts.len(), 3);
879
880        let vote_meta = &instruction.accounts[0];
881        assert_eq!(vote_meta.pubkey, vote_pubkey);
882        assert!(!vote_meta.is_signer);
883        assert!(vote_meta.is_writable);
884
885        let source_meta = &instruction.accounts[1];
886        assert_eq!(source_meta.pubkey, source_pubkey);
887        assert!(source_meta.is_signer);
888        assert!(source_meta.is_writable);
889
890        // The vote program transfers the deposit by invoking the System program,
891        // which the runtime only resolves if it is one of the instruction's accounts.
892        let system_program_meta = &instruction.accounts[2];
893        assert_eq!(system_program_meta.pubkey, system_program::id());
894        assert!(!system_program_meta.is_signer);
895        assert!(!system_program_meta.is_writable);
896    }
897
898    #[test]
899    fn test_builders_match_bincode_encoding() {
900        let vote_pubkey = Pubkey::new_unique();
901        let voter_pubkey = Pubkey::new_unique();
902        let lockouts = VecDeque::from([Lockout::new_with_confirmation_count(1, 2)]);
903        let tower_sync = TowerSync::new(
904            lockouts,
905            Some(0),
906            Hash::new_from_array([1; 32]),
907            Hash::new_from_array([2; 32]),
908        );
909        let vote_init = VoteInit {
910            node_pubkey: Pubkey::new_unique(),
911            authorized_voter: voter_pubkey,
912            authorized_withdrawer: Pubkey::new_unique(),
913            commission: 7,
914        };
915
916        for (ix, expected) in [
917            (
918                tower_sync_switch(
919                    &vote_pubkey,
920                    &voter_pubkey,
921                    tower_sync.clone(),
922                    Hash::default(),
923                ),
924                VoteInstruction::TowerSyncSwitch(tower_sync, Hash::default()),
925            ),
926            (
927                authorize_with_seed(
928                    &vote_pubkey,
929                    &voter_pubkey,
930                    &vote_pubkey,
931                    "seed",
932                    &voter_pubkey,
933                    VoteAuthorize::Withdrawer,
934                ),
935                VoteInstruction::AuthorizeWithSeed(VoteAuthorizeWithSeedArgs {
936                    authorization_type: VoteAuthorize::Withdrawer,
937                    current_authority_derived_key_owner: vote_pubkey,
938                    current_authority_derived_key_seed: "seed".to_string(),
939                    new_authority: voter_pubkey,
940                }),
941            ),
942            (
943                create_account_with_config(
944                    &voter_pubkey,
945                    &vote_pubkey,
946                    &vote_init,
947                    42,
948                    CreateVoteAccountConfig::default(),
949                )
950                .pop()
951                .unwrap(),
952                VoteInstruction::InitializeAccount(vote_init),
953            ),
954        ] {
955            assert_eq!(ix.data, bincode::serialize(&expected).unwrap());
956        }
957    }
958}