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::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    DepositDelegatorRewards { deposit: u64 },
262}
263
264impl VoteInstruction {
265    pub fn is_simple_vote(&self) -> bool {
266        matches!(
267            self,
268            Self::Vote(_)
269                | Self::VoteSwitch(_, _)
270                | Self::UpdateVoteState(_)
271                | Self::UpdateVoteStateSwitch(_, _)
272                | Self::CompactUpdateVoteState(_)
273                | Self::CompactUpdateVoteStateSwitch(_, _)
274                | Self::TowerSync(_)
275                | Self::TowerSyncSwitch(_, _),
276        )
277    }
278
279    pub fn is_single_vote_state_update(&self) -> bool {
280        matches!(
281            self,
282            Self::UpdateVoteState(_)
283                | Self::UpdateVoteStateSwitch(_, _)
284                | Self::CompactUpdateVoteState(_)
285                | Self::CompactUpdateVoteStateSwitch(_, _)
286                | Self::TowerSync(_)
287                | Self::TowerSyncSwitch(_, _),
288        )
289    }
290
291    /// Only to be used on vote instructions (guard with is_simple_vote),  panics otherwise
292    pub fn last_voted_slot(&self) -> Option<Slot> {
293        assert!(self.is_simple_vote());
294        match self {
295            Self::Vote(v) | Self::VoteSwitch(v, _) => v.last_voted_slot(),
296            Self::UpdateVoteState(vote_state_update)
297            | Self::UpdateVoteStateSwitch(vote_state_update, _)
298            | Self::CompactUpdateVoteState(vote_state_update)
299            | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
300                vote_state_update.last_voted_slot()
301            }
302            Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => {
303                tower_sync.last_voted_slot()
304            }
305            _ => panic!("Tried to get slot on non simple vote instruction"),
306        }
307    }
308
309    /// Only to be used on vote instructions (guard with is_simple_vote), panics otherwise
310    pub fn hash(&self) -> Hash {
311        assert!(self.is_simple_vote());
312        let hash = match self {
313            Self::Vote(v) | Self::VoteSwitch(v, _) => &v.hash,
314            Self::UpdateVoteState(vote_state_update)
315            | Self::UpdateVoteStateSwitch(vote_state_update, _)
316            | Self::CompactUpdateVoteState(vote_state_update)
317            | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => &vote_state_update.hash,
318            Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => &tower_sync.hash,
319            _ => panic!("Tried to get hash on non simple vote instruction"),
320        };
321        Hash::new_from_array(hash.to_bytes())
322    }
323    /// Only to be used on vote instructions (guard with is_simple_vote),  panics otherwise
324    pub fn timestamp(&self) -> Option<UnixTimestamp> {
325        assert!(self.is_simple_vote());
326        match self {
327            Self::Vote(v) | Self::VoteSwitch(v, _) => v.timestamp,
328            Self::UpdateVoteState(vote_state_update)
329            | Self::UpdateVoteStateSwitch(vote_state_update, _)
330            | Self::CompactUpdateVoteState(vote_state_update)
331            | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
332                vote_state_update.timestamp
333            }
334            Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => {
335                tower_sync.timestamp
336            }
337            _ => panic!("Tried to get timestamp on non simple vote instruction"),
338        }
339    }
340}
341
342#[cfg(feature = "bincode")]
343fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction {
344    let account_metas = vec![
345        AccountMeta::new(*vote_pubkey, false),
346        AccountMeta::new_readonly(sysvar::rent::id(), false),
347        AccountMeta::new_readonly(sysvar::clock::id(), false),
348        AccountMeta::new_readonly(vote_init.node_pubkey, true),
349    ];
350
351    Instruction::new_with_bincode(
352        id(),
353        &VoteInstruction::InitializeAccount(*vote_init),
354        account_metas,
355    )
356}
357
358#[cfg(feature = "bincode")]
359fn initialize_account_v2(
360    vote_pubkey: &Pubkey,
361    vote_init: &VoteInitV2,
362    inflation_rewards_collector: &Pubkey,
363    block_revenue_collector: &Pubkey,
364) -> Instruction {
365    let account_metas = vec![
366        AccountMeta::new(*vote_pubkey, false),
367        AccountMeta::new_readonly(vote_init.node_pubkey, true),
368        AccountMeta::new(*inflation_rewards_collector, false),
369        AccountMeta::new(*block_revenue_collector, false),
370    ];
371
372    Instruction::new_with_bincode(
373        id(),
374        &VoteInstruction::InitializeAccountV2(*vote_init),
375        account_metas,
376    )
377}
378
379pub struct CreateVoteAccountConfig<'a> {
380    pub space: u64,
381    pub with_seed: Option<(&'a Pubkey, &'a str)>,
382}
383
384impl Default for CreateVoteAccountConfig<'_> {
385    fn default() -> Self {
386        Self {
387            // Create new vote accounts with size for V4.
388            space: VoteStateV4::size_of() as u64,
389            with_seed: None,
390        }
391    }
392}
393
394#[cfg(feature = "bincode")]
395pub fn create_account_with_config(
396    from_pubkey: &Pubkey,
397    vote_pubkey: &Pubkey,
398    vote_init: &VoteInit,
399    lamports: u64,
400    config: CreateVoteAccountConfig,
401) -> Vec<Instruction> {
402    let create_ix = if let Some((base, seed)) = config.with_seed {
403        solana_system_interface::instruction::create_account_with_seed(
404            from_pubkey,
405            vote_pubkey,
406            base,
407            seed,
408            lamports,
409            config.space,
410            &id(),
411        )
412    } else {
413        solana_system_interface::instruction::create_account(
414            from_pubkey,
415            vote_pubkey,
416            lamports,
417            config.space,
418            &id(),
419        )
420    };
421    let init_ix = initialize_account(vote_pubkey, vote_init);
422    vec![create_ix, init_ix]
423}
424
425#[cfg(feature = "bincode")]
426pub fn create_account_with_config_v2(
427    from_pubkey: &Pubkey,
428    vote_pubkey: &Pubkey,
429    vote_init: &VoteInitV2,
430    inflation_rewards_collector: &Pubkey,
431    block_revenue_collector: &Pubkey,
432    lamports: u64,
433    config: CreateVoteAccountConfig,
434) -> Vec<Instruction> {
435    let create_ix = if let Some((base, seed)) = config.with_seed {
436        solana_system_interface::instruction::create_account_with_seed(
437            from_pubkey,
438            vote_pubkey,
439            base,
440            seed,
441            lamports,
442            config.space,
443            &id(),
444        )
445    } else {
446        solana_system_interface::instruction::create_account(
447            from_pubkey,
448            vote_pubkey,
449            lamports,
450            config.space,
451            &id(),
452        )
453    };
454    let init_ix = initialize_account_v2(
455        vote_pubkey,
456        vote_init,
457        inflation_rewards_collector,
458        block_revenue_collector,
459    );
460    vec![create_ix, init_ix]
461}
462
463#[cfg(feature = "bincode")]
464pub fn authorize(
465    vote_pubkey: &Pubkey,
466    authorized_pubkey: &Pubkey, // currently authorized
467    new_authorized_pubkey: &Pubkey,
468    vote_authorize: VoteAuthorize,
469) -> Instruction {
470    let account_metas = vec![
471        AccountMeta::new(*vote_pubkey, false),
472        AccountMeta::new_readonly(sysvar::clock::id(), false),
473        AccountMeta::new_readonly(*authorized_pubkey, true),
474    ];
475
476    Instruction::new_with_bincode(
477        id(),
478        &VoteInstruction::Authorize(*new_authorized_pubkey, vote_authorize),
479        account_metas,
480    )
481}
482
483#[cfg(feature = "bincode")]
484pub fn authorize_checked(
485    vote_pubkey: &Pubkey,
486    authorized_pubkey: &Pubkey, // currently authorized
487    new_authorized_pubkey: &Pubkey,
488    vote_authorize: 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(*authorized_pubkey, true),
494        AccountMeta::new_readonly(*new_authorized_pubkey, true),
495    ];
496
497    Instruction::new_with_bincode(
498        id(),
499        &VoteInstruction::AuthorizeChecked(vote_authorize),
500        account_metas,
501    )
502}
503
504#[cfg(feature = "bincode")]
505pub fn authorize_with_seed(
506    vote_pubkey: &Pubkey,
507    current_authority_base_key: &Pubkey,
508    current_authority_derived_key_owner: &Pubkey,
509    current_authority_derived_key_seed: &str,
510    new_authority: &Pubkey,
511    authorization_type: VoteAuthorize,
512) -> Instruction {
513    let account_metas = vec![
514        AccountMeta::new(*vote_pubkey, false),
515        AccountMeta::new_readonly(sysvar::clock::id(), false),
516        AccountMeta::new_readonly(*current_authority_base_key, true),
517    ];
518
519    Instruction::new_with_bincode(
520        id(),
521        &VoteInstruction::AuthorizeWithSeed(VoteAuthorizeWithSeedArgs {
522            authorization_type,
523            current_authority_derived_key_owner: *current_authority_derived_key_owner,
524            current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
525            new_authority: *new_authority,
526        }),
527        account_metas,
528    )
529}
530
531#[cfg(feature = "bincode")]
532pub fn authorize_checked_with_seed(
533    vote_pubkey: &Pubkey,
534    current_authority_base_key: &Pubkey,
535    current_authority_derived_key_owner: &Pubkey,
536    current_authority_derived_key_seed: &str,
537    new_authority: &Pubkey,
538    authorization_type: VoteAuthorize,
539) -> Instruction {
540    let account_metas = vec![
541        AccountMeta::new(*vote_pubkey, false),
542        AccountMeta::new_readonly(sysvar::clock::id(), false),
543        AccountMeta::new_readonly(*current_authority_base_key, true),
544        AccountMeta::new_readonly(*new_authority, true),
545    ];
546
547    Instruction::new_with_bincode(
548        id(),
549        &VoteInstruction::AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs {
550            authorization_type,
551            current_authority_derived_key_owner: *current_authority_derived_key_owner,
552            current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
553        }),
554        account_metas,
555    )
556}
557
558#[cfg(feature = "bincode")]
559pub fn update_validator_identity(
560    vote_pubkey: &Pubkey,
561    authorized_withdrawer_pubkey: &Pubkey,
562    node_pubkey: &Pubkey,
563) -> Instruction {
564    let account_metas = vec![
565        AccountMeta::new(*vote_pubkey, false),
566        AccountMeta::new_readonly(*node_pubkey, true),
567        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
568    ];
569
570    Instruction::new_with_bincode(
571        id(),
572        &VoteInstruction::UpdateValidatorIdentity,
573        account_metas,
574    )
575}
576
577#[cfg(feature = "bincode")]
578pub fn update_commission(
579    vote_pubkey: &Pubkey,
580    authorized_withdrawer_pubkey: &Pubkey,
581    commission: u8,
582) -> Instruction {
583    let account_metas = vec![
584        AccountMeta::new(*vote_pubkey, false),
585        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
586    ];
587
588    Instruction::new_with_bincode(
589        id(),
590        &VoteInstruction::UpdateCommission(commission),
591        account_metas,
592    )
593}
594
595#[cfg(feature = "bincode")]
596pub fn update_commission_collector(
597    vote_pubkey: &Pubkey,
598    authorized_withdrawer_pubkey: &Pubkey,
599    new_collector_pubkey: &Pubkey,
600    kind: CommissionKind,
601) -> Instruction {
602    let account_metas = vec![
603        AccountMeta::new(*vote_pubkey, false),
604        AccountMeta::new(*new_collector_pubkey, false),
605        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
606    ];
607
608    Instruction::new_with_bincode(
609        id(),
610        &VoteInstruction::UpdateCommissionCollector(kind),
611        account_metas,
612    )
613}
614
615#[cfg(feature = "bincode")]
616pub fn update_commission_bps(
617    vote_pubkey: &Pubkey,
618    authorized_withdrawer_pubkey: &Pubkey,
619    kind: CommissionKind,
620    commission_bps: u16,
621) -> Instruction {
622    let account_metas = vec![
623        AccountMeta::new(*vote_pubkey, false),
624        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
625    ];
626
627    Instruction::new_with_bincode(
628        id(),
629        &VoteInstruction::UpdateCommissionBps {
630            kind,
631            commission_bps,
632        },
633        account_metas,
634    )
635}
636
637#[cfg(feature = "bincode")]
638pub fn deposit_delegator_rewards(
639    vote_pubkey: &Pubkey,
640    source_pubkey: &Pubkey,
641    deposit: u64,
642) -> Instruction {
643    let account_metas = vec![
644        AccountMeta::new(*vote_pubkey, false),
645        AccountMeta::new(*source_pubkey, true),
646    ];
647
648    Instruction::new_with_bincode(
649        id(),
650        &VoteInstruction::DepositDelegatorRewards { deposit },
651        account_metas,
652    )
653}
654
655#[cfg(feature = "bincode")]
656pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction {
657    let account_metas = vec![
658        AccountMeta::new(*vote_pubkey, false),
659        AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
660        AccountMeta::new_readonly(sysvar::clock::id(), false),
661        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
662    ];
663
664    Instruction::new_with_bincode(id(), &VoteInstruction::Vote(vote), account_metas)
665}
666
667#[cfg(feature = "bincode")]
668pub fn vote_switch(
669    vote_pubkey: &Pubkey,
670    authorized_voter_pubkey: &Pubkey,
671    vote: Vote,
672    proof_hash: Hash,
673) -> Instruction {
674    let account_metas = vec![
675        AccountMeta::new(*vote_pubkey, false),
676        AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
677        AccountMeta::new_readonly(sysvar::clock::id(), false),
678        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
679    ];
680
681    Instruction::new_with_bincode(
682        id(),
683        &VoteInstruction::VoteSwitch(vote, proof_hash),
684        account_metas,
685    )
686}
687
688#[cfg(feature = "bincode")]
689pub fn update_vote_state(
690    vote_pubkey: &Pubkey,
691    authorized_voter_pubkey: &Pubkey,
692    vote_state_update: VoteStateUpdate,
693) -> Instruction {
694    let account_metas = vec![
695        AccountMeta::new(*vote_pubkey, false),
696        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
697    ];
698
699    Instruction::new_with_bincode(
700        id(),
701        &VoteInstruction::UpdateVoteState(vote_state_update),
702        account_metas,
703    )
704}
705
706#[cfg(feature = "bincode")]
707pub fn update_vote_state_switch(
708    vote_pubkey: &Pubkey,
709    authorized_voter_pubkey: &Pubkey,
710    vote_state_update: VoteStateUpdate,
711    proof_hash: Hash,
712) -> Instruction {
713    let account_metas = vec![
714        AccountMeta::new(*vote_pubkey, false),
715        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
716    ];
717
718    Instruction::new_with_bincode(
719        id(),
720        &VoteInstruction::UpdateVoteStateSwitch(vote_state_update, proof_hash),
721        account_metas,
722    )
723}
724
725#[cfg(feature = "bincode")]
726pub fn compact_update_vote_state(
727    vote_pubkey: &Pubkey,
728    authorized_voter_pubkey: &Pubkey,
729    vote_state_update: VoteStateUpdate,
730) -> Instruction {
731    let account_metas = vec![
732        AccountMeta::new(*vote_pubkey, false),
733        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
734    ];
735
736    Instruction::new_with_bincode(
737        id(),
738        &VoteInstruction::CompactUpdateVoteState(vote_state_update),
739        account_metas,
740    )
741}
742
743#[cfg(feature = "bincode")]
744pub fn compact_update_vote_state_switch(
745    vote_pubkey: &Pubkey,
746    authorized_voter_pubkey: &Pubkey,
747    vote_state_update: VoteStateUpdate,
748    proof_hash: Hash,
749) -> Instruction {
750    let account_metas = vec![
751        AccountMeta::new(*vote_pubkey, false),
752        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
753    ];
754
755    Instruction::new_with_bincode(
756        id(),
757        &VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, proof_hash),
758        account_metas,
759    )
760}
761
762#[cfg(feature = "bincode")]
763pub fn tower_sync(
764    vote_pubkey: &Pubkey,
765    authorized_voter_pubkey: &Pubkey,
766    tower_sync: TowerSync,
767) -> Instruction {
768    let account_metas = vec![
769        AccountMeta::new(*vote_pubkey, false),
770        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
771    ];
772
773    Instruction::new_with_bincode(id(), &VoteInstruction::TowerSync(tower_sync), account_metas)
774}
775
776#[cfg(feature = "bincode")]
777pub fn tower_sync_switch(
778    vote_pubkey: &Pubkey,
779    authorized_voter_pubkey: &Pubkey,
780    tower_sync: TowerSync,
781    proof_hash: Hash,
782) -> Instruction {
783    let account_metas = vec![
784        AccountMeta::new(*vote_pubkey, false),
785        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
786    ];
787
788    Instruction::new_with_bincode(
789        id(),
790        &VoteInstruction::TowerSyncSwitch(tower_sync, proof_hash),
791        account_metas,
792    )
793}
794
795#[cfg(feature = "bincode")]
796pub fn withdraw(
797    vote_pubkey: &Pubkey,
798    authorized_withdrawer_pubkey: &Pubkey,
799    lamports: u64,
800    to_pubkey: &Pubkey,
801) -> Instruction {
802    let account_metas = vec![
803        AccountMeta::new(*vote_pubkey, false),
804        AccountMeta::new(*to_pubkey, false),
805        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
806    ];
807
808    Instruction::new_with_bincode(id(), &VoteInstruction::Withdraw(lamports), account_metas)
809}
810
811#[cfg(all(test, feature = "bincode"))]
812mod tests {
813    use {super::*, crate::state::Lockout, std::collections::VecDeque};
814
815    #[test]
816    fn test_compact_serialize_rejects_confirmation_count_above_u8() {
817        // The compact wire format stores `confirmation_count` as a `u8`, so a
818        // value that does not fit (> 255) must fail to serialize rather than be
819        // silently truncated.
820        let lockouts = VecDeque::from([Lockout::new_with_confirmation_count(1, 256)]);
821        let vote_state_update = VoteStateUpdate::new(lockouts.clone(), None, Hash::default());
822        let tower_sync = TowerSync::new(lockouts, None, Hash::default(), Hash::default());
823
824        for ix in [
825            VoteInstruction::CompactUpdateVoteState(vote_state_update),
826            VoteInstruction::TowerSync(tower_sync),
827        ] {
828            let err = bincode::serialize(&ix).unwrap_err();
829            assert!(
830                err.to_string().contains("Invalid confirmation count"),
831                "unexpected error: {err}"
832            );
833            #[cfg(feature = "wincode")]
834            assert!(wincode::serialize(&ix).is_err());
835        }
836    }
837}