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, VoteStateVersions,
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            space: VoteStateVersions::vote_state_size_of(false) as u64,
276            with_seed: None,
277        }
278    }
279}
280
281#[cfg(feature = "bincode")]
282pub fn create_account_with_config(
283    from_pubkey: &Pubkey,
284    vote_pubkey: &Pubkey,
285    vote_init: &VoteInit,
286    lamports: u64,
287    config: CreateVoteAccountConfig,
288) -> Vec<Instruction> {
289    let create_ix = if let Some((base, seed)) = config.with_seed {
290        solana_system_interface::instruction::create_account_with_seed(
291            from_pubkey,
292            vote_pubkey,
293            base,
294            seed,
295            lamports,
296            config.space,
297            &id(),
298        )
299    } else {
300        solana_system_interface::instruction::create_account(
301            from_pubkey,
302            vote_pubkey,
303            lamports,
304            config.space,
305            &id(),
306        )
307    };
308    let init_ix = initialize_account(vote_pubkey, vote_init);
309    vec![create_ix, init_ix]
310}
311
312#[cfg(feature = "bincode")]
313pub fn authorize(
314    vote_pubkey: &Pubkey,
315    authorized_pubkey: &Pubkey, // currently authorized
316    new_authorized_pubkey: &Pubkey,
317    vote_authorize: VoteAuthorize,
318) -> Instruction {
319    let account_metas = vec![
320        AccountMeta::new(*vote_pubkey, false),
321        AccountMeta::new_readonly(sysvar::clock::id(), false),
322        AccountMeta::new_readonly(*authorized_pubkey, true),
323    ];
324
325    Instruction::new_with_bincode(
326        id(),
327        &VoteInstruction::Authorize(*new_authorized_pubkey, vote_authorize),
328        account_metas,
329    )
330}
331
332#[cfg(feature = "bincode")]
333pub fn authorize_checked(
334    vote_pubkey: &Pubkey,
335    authorized_pubkey: &Pubkey, // currently authorized
336    new_authorized_pubkey: &Pubkey,
337    vote_authorize: VoteAuthorize,
338) -> Instruction {
339    let account_metas = vec![
340        AccountMeta::new(*vote_pubkey, false),
341        AccountMeta::new_readonly(sysvar::clock::id(), false),
342        AccountMeta::new_readonly(*authorized_pubkey, true),
343        AccountMeta::new_readonly(*new_authorized_pubkey, true),
344    ];
345
346    Instruction::new_with_bincode(
347        id(),
348        &VoteInstruction::AuthorizeChecked(vote_authorize),
349        account_metas,
350    )
351}
352
353#[cfg(feature = "bincode")]
354pub fn authorize_with_seed(
355    vote_pubkey: &Pubkey,
356    current_authority_base_key: &Pubkey,
357    current_authority_derived_key_owner: &Pubkey,
358    current_authority_derived_key_seed: &str,
359    new_authority: &Pubkey,
360    authorization_type: VoteAuthorize,
361) -> Instruction {
362    let account_metas = vec![
363        AccountMeta::new(*vote_pubkey, false),
364        AccountMeta::new_readonly(sysvar::clock::id(), false),
365        AccountMeta::new_readonly(*current_authority_base_key, true),
366    ];
367
368    Instruction::new_with_bincode(
369        id(),
370        &VoteInstruction::AuthorizeWithSeed(VoteAuthorizeWithSeedArgs {
371            authorization_type,
372            current_authority_derived_key_owner: *current_authority_derived_key_owner,
373            current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
374            new_authority: *new_authority,
375        }),
376        account_metas,
377    )
378}
379
380#[cfg(feature = "bincode")]
381pub fn authorize_checked_with_seed(
382    vote_pubkey: &Pubkey,
383    current_authority_base_key: &Pubkey,
384    current_authority_derived_key_owner: &Pubkey,
385    current_authority_derived_key_seed: &str,
386    new_authority: &Pubkey,
387    authorization_type: VoteAuthorize,
388) -> Instruction {
389    let account_metas = vec![
390        AccountMeta::new(*vote_pubkey, false),
391        AccountMeta::new_readonly(sysvar::clock::id(), false),
392        AccountMeta::new_readonly(*current_authority_base_key, true),
393        AccountMeta::new_readonly(*new_authority, true),
394    ];
395
396    Instruction::new_with_bincode(
397        id(),
398        &VoteInstruction::AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs {
399            authorization_type,
400            current_authority_derived_key_owner: *current_authority_derived_key_owner,
401            current_authority_derived_key_seed: current_authority_derived_key_seed.to_string(),
402        }),
403        account_metas,
404    )
405}
406
407#[cfg(feature = "bincode")]
408pub fn update_validator_identity(
409    vote_pubkey: &Pubkey,
410    authorized_withdrawer_pubkey: &Pubkey,
411    node_pubkey: &Pubkey,
412) -> Instruction {
413    let account_metas = vec![
414        AccountMeta::new(*vote_pubkey, false),
415        AccountMeta::new_readonly(*node_pubkey, true),
416        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
417    ];
418
419    Instruction::new_with_bincode(
420        id(),
421        &VoteInstruction::UpdateValidatorIdentity,
422        account_metas,
423    )
424}
425
426#[cfg(feature = "bincode")]
427pub fn update_commission(
428    vote_pubkey: &Pubkey,
429    authorized_withdrawer_pubkey: &Pubkey,
430    commission: u8,
431) -> Instruction {
432    let account_metas = vec![
433        AccountMeta::new(*vote_pubkey, false),
434        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
435    ];
436
437    Instruction::new_with_bincode(
438        id(),
439        &VoteInstruction::UpdateCommission(commission),
440        account_metas,
441    )
442}
443
444#[cfg(feature = "bincode")]
445pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction {
446    let account_metas = vec![
447        AccountMeta::new(*vote_pubkey, false),
448        AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
449        AccountMeta::new_readonly(sysvar::clock::id(), false),
450        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
451    ];
452
453    Instruction::new_with_bincode(id(), &VoteInstruction::Vote(vote), account_metas)
454}
455
456#[cfg(feature = "bincode")]
457pub fn vote_switch(
458    vote_pubkey: &Pubkey,
459    authorized_voter_pubkey: &Pubkey,
460    vote: Vote,
461    proof_hash: Hash,
462) -> Instruction {
463    let account_metas = vec![
464        AccountMeta::new(*vote_pubkey, false),
465        AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
466        AccountMeta::new_readonly(sysvar::clock::id(), false),
467        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
468    ];
469
470    Instruction::new_with_bincode(
471        id(),
472        &VoteInstruction::VoteSwitch(vote, proof_hash),
473        account_metas,
474    )
475}
476
477#[cfg(feature = "bincode")]
478pub fn update_vote_state(
479    vote_pubkey: &Pubkey,
480    authorized_voter_pubkey: &Pubkey,
481    vote_state_update: VoteStateUpdate,
482) -> Instruction {
483    let account_metas = vec![
484        AccountMeta::new(*vote_pubkey, false),
485        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
486    ];
487
488    Instruction::new_with_bincode(
489        id(),
490        &VoteInstruction::UpdateVoteState(vote_state_update),
491        account_metas,
492    )
493}
494
495#[cfg(feature = "bincode")]
496pub fn update_vote_state_switch(
497    vote_pubkey: &Pubkey,
498    authorized_voter_pubkey: &Pubkey,
499    vote_state_update: VoteStateUpdate,
500    proof_hash: Hash,
501) -> Instruction {
502    let account_metas = vec![
503        AccountMeta::new(*vote_pubkey, false),
504        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
505    ];
506
507    Instruction::new_with_bincode(
508        id(),
509        &VoteInstruction::UpdateVoteStateSwitch(vote_state_update, proof_hash),
510        account_metas,
511    )
512}
513
514#[cfg(feature = "bincode")]
515pub fn compact_update_vote_state(
516    vote_pubkey: &Pubkey,
517    authorized_voter_pubkey: &Pubkey,
518    vote_state_update: VoteStateUpdate,
519) -> Instruction {
520    let account_metas = vec![
521        AccountMeta::new(*vote_pubkey, false),
522        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
523    ];
524
525    Instruction::new_with_bincode(
526        id(),
527        &VoteInstruction::CompactUpdateVoteState(vote_state_update),
528        account_metas,
529    )
530}
531
532#[cfg(feature = "bincode")]
533pub fn compact_update_vote_state_switch(
534    vote_pubkey: &Pubkey,
535    authorized_voter_pubkey: &Pubkey,
536    vote_state_update: VoteStateUpdate,
537    proof_hash: Hash,
538) -> Instruction {
539    let account_metas = vec![
540        AccountMeta::new(*vote_pubkey, false),
541        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
542    ];
543
544    Instruction::new_with_bincode(
545        id(),
546        &VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, proof_hash),
547        account_metas,
548    )
549}
550
551#[cfg(feature = "bincode")]
552pub fn tower_sync(
553    vote_pubkey: &Pubkey,
554    authorized_voter_pubkey: &Pubkey,
555    tower_sync: TowerSync,
556) -> Instruction {
557    let account_metas = vec![
558        AccountMeta::new(*vote_pubkey, false),
559        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
560    ];
561
562    Instruction::new_with_bincode(id(), &VoteInstruction::TowerSync(tower_sync), account_metas)
563}
564
565#[cfg(feature = "bincode")]
566pub fn tower_sync_switch(
567    vote_pubkey: &Pubkey,
568    authorized_voter_pubkey: &Pubkey,
569    tower_sync: TowerSync,
570    proof_hash: Hash,
571) -> Instruction {
572    let account_metas = vec![
573        AccountMeta::new(*vote_pubkey, false),
574        AccountMeta::new_readonly(*authorized_voter_pubkey, true),
575    ];
576
577    Instruction::new_with_bincode(
578        id(),
579        &VoteInstruction::TowerSyncSwitch(tower_sync, proof_hash),
580        account_metas,
581    )
582}
583
584#[cfg(feature = "bincode")]
585pub fn withdraw(
586    vote_pubkey: &Pubkey,
587    authorized_withdrawer_pubkey: &Pubkey,
588    lamports: u64,
589    to_pubkey: &Pubkey,
590) -> Instruction {
591    let account_metas = vec![
592        AccountMeta::new(*vote_pubkey, false),
593        AccountMeta::new(*to_pubkey, false),
594        AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
595    ];
596
597    Instruction::new_with_bincode(id(), &VoteInstruction::Withdraw(lamports), account_metas)
598}