solana_program/vote/
instruction.rs

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