solana_vote_interface/
instruction.rs

1//! Vote program instructions
2
3use {
4    super::state::TowerSync,
5    crate::state::{
6        Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs, VoteAuthorizeWithSeedArgs, VoteInit,
7        VoteStateUpdate, VoteStateV4,
8    },
9    solana_clock::{Slot, UnixTimestamp},
10    solana_hash::Hash,
11    solana_pubkey::Pubkey,
12};
13#[cfg(feature = "bincode")]
14use {
15    crate::program::id,
16    solana_instruction::{AccountMeta, Instruction},
17    solana_sdk_ids::sysvar,
18};
19#[cfg(feature = "serde")]
20use {
21    crate::state::{serde_compact_vote_state_update, serde_tower_sync},
22    serde_derive::{Deserialize, Serialize},
23};
24
25#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
26#[derive(Debug, PartialEq, Eq, Clone)]
27pub enum VoteInstruction {
28    /// Initialize a vote account
29    ///
30    /// # Account references
31    ///   0. `[WRITE]` Uninitialized vote account
32    ///   1. `[]` Rent sysvar
33    ///   2. `[]` Clock sysvar
34    ///   3. `[SIGNER]` New validator identity (node_pubkey)
35    InitializeAccount(VoteInit),
36
37    /// Authorize a key to send votes or issue a withdrawal
38    ///
39    /// # Account references
40    ///   0. `[WRITE]` Vote account to be updated with the Pubkey for authorization
41    ///   1. `[]` Clock sysvar
42    ///   2. `[SIGNER]` Vote or withdraw authority
43    Authorize(Pubkey, VoteAuthorize),
44
45    /// A Vote instruction with recent votes
46    ///
47    /// # Account references
48    ///   0. `[WRITE]` Vote account to vote with
49    ///   1. `[]` Slot hashes sysvar
50    ///   2. `[]` Clock sysvar
51    ///   3. `[SIGNER]` Vote authority
52    Vote(Vote),
53
54    /// Withdraw some amount of funds
55    ///
56    /// # Account references
57    ///   0. `[WRITE]` Vote account to withdraw from
58    ///   1. `[WRITE]` Recipient account
59    ///   2. `[SIGNER]` Withdraw authority
60    Withdraw(u64),
61
62    /// Update the vote account's validator identity (node_pubkey)
63    ///
64    /// # Account references
65    ///   0. `[WRITE]` Vote account to be updated with the given authority public key
66    ///   1. `[SIGNER]` New validator identity (node_pubkey)
67    ///   2. `[SIGNER]` Withdraw authority
68    UpdateValidatorIdentity,
69
70    /// Update the commission for the vote account
71    ///
72    /// # Account references
73    ///   0. `[WRITE]` Vote account to be updated
74    ///   1. `[SIGNER]` Withdraw authority
75    UpdateCommission(u8),
76
77    /// A Vote instruction with recent votes
78    ///
79    /// # Account references
80    ///   0. `[WRITE]` Vote account to vote with
81    ///   1. `[]` Slot hashes sysvar
82    ///   2. `[]` Clock sysvar
83    ///   3. `[SIGNER]` Vote authority
84    VoteSwitch(Vote, Hash),
85
86    /// Authorize a key to send votes or issue a withdrawal
87    ///
88    /// This instruction behaves like `Authorize` with the additional requirement that the new vote
89    /// or withdraw authority must also be a signer.
90    ///
91    /// # Account references
92    ///   0. `[WRITE]` Vote account to be updated with the Pubkey for authorization
93    ///   1. `[]` Clock sysvar
94    ///   2. `[SIGNER]` Vote or withdraw authority
95    ///   3. `[SIGNER]` New vote or withdraw authority
96    AuthorizeChecked(VoteAuthorize),
97
98    /// Update the onchain vote state for the signer.
99    ///
100    /// # Account references
101    ///   0. `[Write]` Vote account to vote with
102    ///   1. `[SIGNER]` Vote authority
103    UpdateVoteState(VoteStateUpdate),
104
105    /// Update the onchain vote state for the signer along with a switching proof.
106    ///
107    /// # Account references
108    ///   0. `[Write]` Vote account to vote with
109    ///   1. `[SIGNER]` Vote authority
110    UpdateVoteStateSwitch(VoteStateUpdate, Hash),
111
112    /// Given that the current Voter or Withdrawer authority is a derived key,
113    /// this instruction allows someone who can sign for that derived key's
114    /// base key to authorize a new Voter or Withdrawer for a vote account.
115    ///
116    /// # Account references
117    ///   0. `[Write]` Vote account to be updated
118    ///   1. `[]` Clock sysvar
119    ///   2. `[SIGNER]` Base key of current Voter or Withdrawer authority's derived key
120    AuthorizeWithSeed(VoteAuthorizeWithSeedArgs),
121
122    /// Given that the current Voter or Withdrawer authority is a derived key,
123    /// this instruction allows someone who can sign for that derived key's
124    /// base key to authorize a new Voter or Withdrawer for a vote account.
125    ///
126    /// This instruction behaves like `AuthorizeWithSeed` with the additional requirement
127    /// that the new vote or withdraw authority must also be a signer.
128    ///
129    /// # Account references
130    ///   0. `[Write]` Vote account to be updated
131    ///   1. `[]` Clock sysvar
132    ///   2. `[SIGNER]` Base key of current Voter or Withdrawer authority's derived key
133    ///   3. `[SIGNER]` New vote or withdraw authority
134    AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs),
135
136    /// Update the onchain vote state for the signer.
137    ///
138    /// # Account references
139    ///   0. `[Write]` Vote account to vote with
140    ///   1. `[SIGNER]` Vote authority
141    #[cfg_attr(feature = "serde", serde(with = "serde_compact_vote_state_update"))]
142    CompactUpdateVoteState(VoteStateUpdate),
143
144    /// Update the onchain vote state for the signer along with a switching proof.
145    ///
146    /// # Account references
147    ///   0. `[Write]` Vote account to vote with
148    ///   1. `[SIGNER]` Vote authority
149    CompactUpdateVoteStateSwitch(
150        #[cfg_attr(feature = "serde", serde(with = "serde_compact_vote_state_update"))]
151        VoteStateUpdate,
152        Hash,
153    ),
154
155    /// Sync the onchain vote state with local tower
156    ///
157    /// # Account references
158    ///   0. `[Write]` Vote account to vote with
159    ///   1. `[SIGNER]` Vote authority
160    #[cfg_attr(feature = "serde", serde(with = "serde_tower_sync"))]
161    TowerSync(TowerSync),
162
163    /// Sync the onchain vote state with local tower along with a switching proof
164    ///
165    /// # Account references
166    ///   0. `[Write]` Vote account to vote with
167    ///   1. `[SIGNER]` Vote authority
168    TowerSyncSwitch(
169        #[cfg_attr(feature = "serde", serde(with = "serde_tower_sync"))] TowerSync,
170        Hash,
171    ),
172}
173
174impl VoteInstruction {
175    pub fn is_simple_vote(&self) -> bool {
176        matches!(
177            self,
178            Self::Vote(_)
179                | Self::VoteSwitch(_, _)
180                | Self::UpdateVoteState(_)
181                | Self::UpdateVoteStateSwitch(_, _)
182                | Self::CompactUpdateVoteState(_)
183                | Self::CompactUpdateVoteStateSwitch(_, _)
184                | Self::TowerSync(_)
185                | Self::TowerSyncSwitch(_, _),
186        )
187    }
188
189    pub fn is_single_vote_state_update(&self) -> bool {
190        matches!(
191            self,
192            Self::UpdateVoteState(_)
193                | Self::UpdateVoteStateSwitch(_, _)
194                | Self::CompactUpdateVoteState(_)
195                | Self::CompactUpdateVoteStateSwitch(_, _)
196                | Self::TowerSync(_)
197                | Self::TowerSyncSwitch(_, _),
198        )
199    }
200
201    /// Only to be used on vote instructions (guard with is_simple_vote),  panics otherwise
202    pub fn last_voted_slot(&self) -> Option<Slot> {
203        assert!(self.is_simple_vote());
204        match self {
205            Self::Vote(v) | Self::VoteSwitch(v, _) => v.last_voted_slot(),
206            Self::UpdateVoteState(vote_state_update)
207            | Self::UpdateVoteStateSwitch(vote_state_update, _)
208            | Self::CompactUpdateVoteState(vote_state_update)
209            | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
210                vote_state_update.last_voted_slot()
211            }
212            Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => {
213                tower_sync.last_voted_slot()
214            }
215            _ => panic!("Tried to get slot on non simple vote instruction"),
216        }
217    }
218
219    /// Only to be used on vote instructions (guard with is_simple_vote), panics otherwise
220    pub fn hash(&self) -> Hash {
221        assert!(self.is_simple_vote());
222        match self {
223            Self::Vote(v) | Self::VoteSwitch(v, _) => v.hash,
224            Self::UpdateVoteState(vote_state_update)
225            | Self::UpdateVoteStateSwitch(vote_state_update, _)
226            | Self::CompactUpdateVoteState(vote_state_update)
227            | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => vote_state_update.hash,
228            Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => tower_sync.hash,
229            _ => panic!("Tried to get hash on non simple vote instruction"),
230        }
231    }
232    /// Only to be used on vote instructions (guard with is_simple_vote),  panics otherwise
233    pub fn timestamp(&self) -> Option<UnixTimestamp> {
234        assert!(self.is_simple_vote());
235        match self {
236            Self::Vote(v) | Self::VoteSwitch(v, _) => v.timestamp,
237            Self::UpdateVoteState(vote_state_update)
238            | Self::UpdateVoteStateSwitch(vote_state_update, _)
239            | Self::CompactUpdateVoteState(vote_state_update)
240            | Self::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
241                vote_state_update.timestamp
242            }
243            Self::TowerSync(tower_sync) | Self::TowerSyncSwitch(tower_sync, _) => {
244                tower_sync.timestamp
245            }
246            _ => panic!("Tried to get timestamp on non simple vote instruction"),
247        }
248    }
249}
250
251#[cfg(feature = "bincode")]
252fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction {
253    let account_metas = vec![
254        AccountMeta::new(*vote_pubkey, false),
255        AccountMeta::new_readonly(sysvar::rent::id(), false),
256        AccountMeta::new_readonly(sysvar::clock::id(), false),
257        AccountMeta::new_readonly(vote_init.node_pubkey, true),
258    ];
259
260    Instruction::new_with_bincode(
261        id(),
262        &VoteInstruction::InitializeAccount(*vote_init),
263        account_metas,
264    )
265}
266
267pub struct CreateVoteAccountConfig<'a> {
268    pub space: u64,
269    pub with_seed: Option<(&'a Pubkey, &'a str)>,
270}
271
272impl Default for CreateVoteAccountConfig<'_> {
273    fn default() -> Self {
274        Self {
275            // Create new vote accounts with size for V4.
276            space: VoteStateV4::size_of() as u64,
277            with_seed: None,
278        }
279    }
280}
281
282#[cfg(feature = "bincode")]
283pub fn create_account_with_config(
284    from_pubkey: &Pubkey,
285    vote_pubkey: &Pubkey,
286    vote_init: &VoteInit,
287    lamports: u64,
288    config: CreateVoteAccountConfig,
289) -> Vec<Instruction> {
290    let create_ix = if let Some((base, seed)) = config.with_seed {
291        solana_system_interface::instruction::create_account_with_seed(
292            from_pubkey,
293            vote_pubkey,
294            base,
295            seed,
296            lamports,
297            config.space,
298            &id(),
299        )
300    } else {
301        solana_system_interface::instruction::create_account(
302            from_pubkey,
303            vote_pubkey,
304            lamports,
305            config.space,
306            &id(),
307        )
308    };
309    let init_ix = initialize_account(vote_pubkey, vote_init);
310    vec![create_ix, init_ix]
311}
312
313#[cfg(feature = "bincode")]
314pub fn authorize(
315    vote_pubkey: &Pubkey,
316    authorized_pubkey: &Pubkey, // currently authorized
317    new_authorized_pubkey: &Pubkey,
318    vote_authorize: VoteAuthorize,
319) -> Instruction {
320    let account_metas = vec![
321        AccountMeta::new(*vote_pubkey, false),
322        AccountMeta::new_readonly(sysvar::clock::id(), false),
323        AccountMeta::new_readonly(*authorized_pubkey, true),
324    ];
325
326    Instruction::new_with_bincode(
327        id(),
328        &VoteInstruction::Authorize(*new_authorized_pubkey, vote_authorize),
329        account_metas,
330    )
331}
332
333#[cfg(feature = "bincode")]
334pub fn authorize_checked(
335    vote_pubkey: &Pubkey,
336    authorized_pubkey: &Pubkey, // currently authorized
337    new_authorized_pubkey: &Pubkey,
338    vote_authorize: VoteAuthorize,
339) -> Instruction {
340    let account_metas = vec![
341        AccountMeta::new(*vote_pubkey, false),
342        AccountMeta::new_readonly(sysvar::clock::id(), false),
343        AccountMeta::new_readonly(*authorized_pubkey, true),
344        AccountMeta::new_readonly(*new_authorized_pubkey, true),
345    ];
346
347    Instruction::new_with_bincode(
348        id(),
349        &VoteInstruction::AuthorizeChecked(vote_authorize),
350        account_metas,
351    )
352}
353
354#[cfg(feature = "bincode")]
355pub fn authorize_with_seed(
356    vote_pubkey: &Pubkey,
357    current_authority_base_key: &Pubkey,
358    current_authority_derived_key_owner: &Pubkey,
359    current_authority_derived_key_seed: &str,
360    new_authority: &Pubkey,
361    authorization_type: VoteAuthorize,
362) -> Instruction {
363    let account_metas = vec![
364        AccountMeta::new(*vote_pubkey, false),
365        AccountMeta::new_readonly(sysvar::clock::id(), false),
366        AccountMeta::new_readonly(*current_authority_base_key, true),
367    ];
368
369    Instruction::new_with_bincode(
370        id(),
371        &VoteInstruction::AuthorizeWithSeed(VoteAuthorizeWithSeedArgs {
372            authorization_type,
373            current_authority_derived_key_owner: *current_authority_derived_key_owner,
374            current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
375            new_authority: *new_authority,
376        }),
377        account_metas,
378    )
379}
380
381#[cfg(feature = "bincode")]
382pub fn authorize_checked_with_seed(
383    vote_pubkey: &Pubkey,
384    current_authority_base_key: &Pubkey,
385    current_authority_derived_key_owner: &Pubkey,
386    current_authority_derived_key_seed: &str,
387    new_authority: &Pubkey,
388    authorization_type: VoteAuthorize,
389) -> Instruction {
390    let account_metas = vec![
391        AccountMeta::new(*vote_pubkey, false),
392        AccountMeta::new_readonly(sysvar::clock::id(), false),
393        AccountMeta::new_readonly(*current_authority_base_key, true),
394        AccountMeta::new_readonly(*new_authority, true),
395    ];
396
397    Instruction::new_with_bincode(
398        id(),
399        &VoteInstruction::AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs {
400            authorization_type,
401            current_authority_derived_key_owner: *current_authority_derived_key_owner,
402            current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
403        }),
404        account_metas,
405    )
406}
407
408#[cfg(feature = "bincode")]
409pub fn update_validator_identity(
410    vote_pubkey: &Pubkey,
411    authorized_withdrawer_pubkey: &Pubkey,
412    node_pubkey: &Pubkey,
413) -> Instruction {
414    let account_metas = vec![
415        AccountMeta::new(*vote_pubkey, false),
416        AccountMeta::new_readonly(*node_pubkey, true),
417        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
418    ];
419
420    Instruction::new_with_bincode(
421        id(),
422        &VoteInstruction::UpdateValidatorIdentity,
423        account_metas,
424    )
425}
426
427#[cfg(feature = "bincode")]
428pub fn update_commission(
429    vote_pubkey: &Pubkey,
430    authorized_withdrawer_pubkey: &Pubkey,
431    commission: u8,
432) -> Instruction {
433    let account_metas = vec![
434        AccountMeta::new(*vote_pubkey, false),
435        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
436    ];
437
438    Instruction::new_with_bincode(
439        id(),
440        &VoteInstruction::UpdateCommission(commission),
441        account_metas,
442    )
443}
444
445#[cfg(feature = "bincode")]
446pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction {
447    let account_metas = vec![
448        AccountMeta::new(*vote_pubkey, false),
449        AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
450        AccountMeta::new_readonly(sysvar::clock::id(), false),
451        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
452    ];
453
454    Instruction::new_with_bincode(id(), &VoteInstruction::Vote(vote), account_metas)
455}
456
457#[cfg(feature = "bincode")]
458pub fn vote_switch(
459    vote_pubkey: &Pubkey,
460    authorized_voter_pubkey: &Pubkey,
461    vote: Vote,
462    proof_hash: Hash,
463) -> Instruction {
464    let account_metas = vec![
465        AccountMeta::new(*vote_pubkey, false),
466        AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
467        AccountMeta::new_readonly(sysvar::clock::id(), false),
468        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
469    ];
470
471    Instruction::new_with_bincode(
472        id(),
473        &VoteInstruction::VoteSwitch(vote, proof_hash),
474        account_metas,
475    )
476}
477
478#[cfg(feature = "bincode")]
479pub fn update_vote_state(
480    vote_pubkey: &Pubkey,
481    authorized_voter_pubkey: &Pubkey,
482    vote_state_update: VoteStateUpdate,
483) -> Instruction {
484    let account_metas = vec![
485        AccountMeta::new(*vote_pubkey, false),
486        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
487    ];
488
489    Instruction::new_with_bincode(
490        id(),
491        &VoteInstruction::UpdateVoteState(vote_state_update),
492        account_metas,
493    )
494}
495
496#[cfg(feature = "bincode")]
497pub fn update_vote_state_switch(
498    vote_pubkey: &Pubkey,
499    authorized_voter_pubkey: &Pubkey,
500    vote_state_update: VoteStateUpdate,
501    proof_hash: Hash,
502) -> Instruction {
503    let account_metas = vec![
504        AccountMeta::new(*vote_pubkey, false),
505        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
506    ];
507
508    Instruction::new_with_bincode(
509        id(),
510        &VoteInstruction::UpdateVoteStateSwitch(vote_state_update, proof_hash),
511        account_metas,
512    )
513}
514
515#[cfg(feature = "bincode")]
516pub fn compact_update_vote_state(
517    vote_pubkey: &Pubkey,
518    authorized_voter_pubkey: &Pubkey,
519    vote_state_update: VoteStateUpdate,
520) -> Instruction {
521    let account_metas = vec![
522        AccountMeta::new(*vote_pubkey, false),
523        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
524    ];
525
526    Instruction::new_with_bincode(
527        id(),
528        &VoteInstruction::CompactUpdateVoteState(vote_state_update),
529        account_metas,
530    )
531}
532
533#[cfg(feature = "bincode")]
534pub fn compact_update_vote_state_switch(
535    vote_pubkey: &Pubkey,
536    authorized_voter_pubkey: &Pubkey,
537    vote_state_update: VoteStateUpdate,
538    proof_hash: Hash,
539) -> Instruction {
540    let account_metas = vec![
541        AccountMeta::new(*vote_pubkey, false),
542        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
543    ];
544
545    Instruction::new_with_bincode(
546        id(),
547        &VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, proof_hash),
548        account_metas,
549    )
550}
551
552#[cfg(feature = "bincode")]
553pub fn tower_sync(
554    vote_pubkey: &Pubkey,
555    authorized_voter_pubkey: &Pubkey,
556    tower_sync: TowerSync,
557) -> Instruction {
558    let account_metas = vec![
559        AccountMeta::new(*vote_pubkey, false),
560        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
561    ];
562
563    Instruction::new_with_bincode(id(), &VoteInstruction::TowerSync(tower_sync), account_metas)
564}
565
566#[cfg(feature = "bincode")]
567pub fn tower_sync_switch(
568    vote_pubkey: &Pubkey,
569    authorized_voter_pubkey: &Pubkey,
570    tower_sync: TowerSync,
571    proof_hash: Hash,
572) -> Instruction {
573    let account_metas = vec![
574        AccountMeta::new(*vote_pubkey, false),
575        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
576    ];
577
578    Instruction::new_with_bincode(
579        id(),
580        &VoteInstruction::TowerSyncSwitch(tower_sync, proof_hash),
581        account_metas,
582    )
583}
584
585#[cfg(feature = "bincode")]
586pub fn withdraw(
587    vote_pubkey: &Pubkey,
588    authorized_withdrawer_pubkey: &Pubkey,
589    lamports: u64,
590    to_pubkey: &Pubkey,
591) -> Instruction {
592    let account_metas = vec![
593        AccountMeta::new(*vote_pubkey, false),
594        AccountMeta::new(*to_pubkey, false),
595        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
596    ];
597
598    Instruction::new_with_bincode(id(), &VoteInstruction::Withdraw(lamports), account_metas)
599}