Skip to main content

solana_vote_interface/
instruction.rs

1//! Vote program instructions
2
3#[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    /// 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    InitializeAccount(VoteInit),
62
63    /// Authorize a key to send votes or issue a withdrawal
64    ///
65    /// # Account references
66    ///   0. `[WRITE]` Vote account to be updated with the Pubkey for authorization
67    ///   1. `[]` Clock sysvar
68    ///   2. `[SIGNER]` Vote or withdraw authority
69    ///
70    /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant is
71    /// disallowed for any vote accounts whose BLS pubkey is set to `Some`.
72    Authorize(Pubkey, VoteAuthorize),
73
74    /// A Vote instruction with recent votes
75    ///
76    /// # Account references
77    ///   0. `[WRITE]` Vote account to vote with
78    ///   1. `[]` Slot hashes sysvar
79    ///   2. `[]` Clock sysvar
80    ///   3. `[SIGNER]` Vote authority
81    Vote(Vote),
82
83    /// Withdraw some amount of funds
84    ///
85    /// # Account references
86    ///   0. `[WRITE]` Vote account to withdraw from
87    ///   1. `[WRITE]` Recipient account
88    ///   2. `[SIGNER]` Withdraw authority
89    Withdraw(u64),
90
91    /// Update the vote account's validator identity (node_pubkey)
92    ///
93    /// # Account references
94    ///   0. `[WRITE]` Vote account to be updated with the given authority public key
95    ///   1. `[SIGNER]` New validator identity (node_pubkey)
96    ///   2. `[SIGNER]` Withdraw authority
97    UpdateValidatorIdentity,
98
99    /// Update the commission for the vote account
100    ///
101    /// # Account references
102    ///   0. `[WRITE]` Vote account to be updated
103    ///   1. `[SIGNER]` Withdraw authority
104    UpdateCommission(u8),
105
106    /// A Vote instruction with recent votes
107    ///
108    /// # Account references
109    ///   0. `[WRITE]` Vote account to vote with
110    ///   1. `[]` Slot hashes sysvar
111    ///   2. `[]` Clock sysvar
112    ///   3. `[SIGNER]` Vote authority
113    VoteSwitch(Vote, Hash),
114
115    /// Authorize a key to send votes or issue a withdrawal
116    ///
117    /// This instruction behaves like `Authorize` with the additional requirement that the new vote
118    /// or withdraw authority must also be a signer.
119    ///
120    /// # Account references
121    ///   0. `[WRITE]` Vote account to be updated with the Pubkey for authorization
122    ///   1. `[]` Clock sysvar
123    ///   2. `[SIGNER]` Vote or withdraw authority
124    ///   3. `[SIGNER]` New vote or withdraw authority
125    ///
126    /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant is
127    /// disallowed for any vote accounts whose BLS pubkey is set to `Some`.
128    AuthorizeChecked(VoteAuthorize),
129
130    /// Update the onchain vote state for the signer.
131    ///
132    /// # Account references
133    ///   0. `[Write]` Vote account to vote with
134    ///   1. `[SIGNER]` Vote authority
135    UpdateVoteState(VoteStateUpdate),
136
137    /// Update the onchain vote state for the signer along with a switching proof.
138    ///
139    /// # Account references
140    ///   0. `[Write]` Vote account to vote with
141    ///   1. `[SIGNER]` Vote authority
142    UpdateVoteStateSwitch(VoteStateUpdate, Hash),
143
144    /// Given that the current Voter or Withdrawer authority is a derived key,
145    /// this instruction allows someone who can sign for that derived key's
146    /// base key to authorize a new Voter or Withdrawer for a vote account.
147    ///
148    /// # Account references
149    ///   0. `[Write]` Vote account to be updated
150    ///   1. `[]` Clock sysvar
151    ///   2. `[SIGNER]` Base key of current Voter or Withdrawer authority's derived key
152    ///
153    /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant in
154    /// `authorization_type` is disallowed for any vote accounts whose BLS
155    /// pubkey is set to `Some`.
156    AuthorizeWithSeed(VoteAuthorizeWithSeedArgs),
157
158    /// Given that the current Voter or Withdrawer authority is a derived key,
159    /// this instruction allows someone who can sign for that derived key's
160    /// base key to authorize a new Voter or Withdrawer for a vote account.
161    ///
162    /// This instruction behaves like `AuthorizeWithSeed` with the additional requirement
163    /// that the new vote or withdraw authority must also be a signer.
164    ///
165    /// # Account references
166    ///   0. `[Write]` Vote account to be updated
167    ///   1. `[]` Clock sysvar
168    ///   2. `[SIGNER]` Base key of current Voter or Withdrawer authority's derived key
169    ///   3. `[SIGNER]` New vote or withdraw authority
170    ///
171    /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant in
172    /// `authorization_type` is disallowed for any vote accounts whose BLS
173    /// pubkey is set to `Some`.
174    AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs),
175
176    /// Update the onchain vote state for the signer.
177    ///
178    /// # Account references
179    ///   0. `[Write]` Vote account to vote with
180    ///   1. `[SIGNER]` Vote authority
181    #[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    /// Update the onchain vote state for the signer along with a switching proof.
187    ///
188    /// # Account references
189    ///   0. `[Write]` Vote account to vote with
190    ///   1. `[SIGNER]` Vote authority
191    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    /// Sync the onchain vote state with local tower
199    ///
200    /// # Account references
201    ///   0. `[Write]` Vote account to vote with
202    ///   1. `[SIGNER]` Vote authority
203    #[cfg_attr(feature = "serde", serde(with = "serde_tower_sync"))]
204    TowerSync(#[cfg_attr(feature = "wincode", wincode(with = "CompactTowerSync"))] TowerSync),
205
206    /// Sync the onchain vote state with local tower along with a switching proof
207    ///
208    /// # Account references
209    ///   0. `[Write]` Vote account to vote with
210    ///   1. `[SIGNER]` Vote authority
211    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    /// Initialize a vote account with all vote state v4 fields, including
219    /// BLS public key and collector accounts.
220    ///
221    /// The vote program performs BLS proof-of-possession verification on the
222    /// submitted BLS public key (per SIMD-0387). For each collector account
223    /// (indices 2 and 3), the program performs the same validation as
224    /// `UpdateCommissionCollector` (per SIMD-0232): if the collector is not
225    /// the vote account itself, it must be system-program owned, rent-exempt,
226    /// and writable (not a reserved account).
227    ///
228    /// # Account references
229    ///   0. `[WRITE]` Uninitialized vote account
230    ///   1. `[SIGNER]` New validator identity (node_pubkey)
231    ///   2. `[WRITE]` Inflation rewards collector
232    ///   3. `[WRITE]` Block revenue collector
233    InitializeAccountV2(VoteInitV2),
234
235    /// Update the commission collector for the vote account
236    ///
237    /// # Account references
238    ///   0. `[WRITE]` Vote account to be updated with the new collector public key
239    ///   1. `[WRITE]` New collector account. Must be set to the vote account or
240    ///      a system program owned account. Must be writable to ensure the
241    ///      account is not reserved.
242    ///   2. `[SIGNER]` Vote account withdraw authority
243    UpdateCommissionCollector(CommissionKind),
244
245    /// Update the commission rate in basis points for the specified commission
246    /// rate kind in a vote account.
247    ///
248    /// # Account references
249    ///   0. `[WRITE]` Vote account to be updated with the new commission
250    ///   1. `[SIGNER]` Vote account withdraw authority
251    UpdateCommissionBps {
252        commission_bps: u16,
253        kind: CommissionKind,
254    },
255
256    /// Deposit lamports for distribution to stake delegators
257    ///
258    /// # Account references
259    ///   0. `[WRITE]` Vote account to be updated with the deposit
260    ///   1. `[SIGNER, WRITE]` Source account for deposit funds
261    ///   2. `[]` System program, used to transfer the deposit
262    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    /// Only to be used on vote instructions (guard with is_simple_vote),  panics otherwise
293    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    /// Only to be used on vote instructions (guard with is_simple_vote), panics otherwise
311    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    /// Only to be used on vote instructions (guard with is_simple_vote),  panics otherwise
325    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            // Create new vote accounts with size for V4.
389            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, // currently authorized
468    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, // currently authorized
488    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        // The compact wire format stores `confirmation_count` as a `u8`, so a
820        // value that does not fit (> 255) must fail to serialize rather than be
821        // silently truncated.
822        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        // The vote program transfers the deposit by invoking the System program,
861        // which the runtime only resolves if it is one of the instruction's accounts.
862        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}