1use {
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 InitializeAccount(VoteInit),
36
37 Authorize(Pubkey, VoteAuthorize),
44
45 Vote(Vote),
53
54 Withdraw(u64),
61
62 UpdateValidatorIdentity,
69
70 UpdateCommission(u8),
76
77 VoteSwitch(Vote, Hash),
85
86 AuthorizeChecked(VoteAuthorize),
97
98 UpdateVoteState(VoteStateUpdate),
104
105 UpdateVoteStateSwitch(VoteStateUpdate, Hash),
111
112 AuthorizeWithSeed(VoteAuthorizeWithSeedArgs),
121
122 AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs),
135
136 #[cfg_attr(feature = "serde", serde(with = "serde_compact_vote_state_update"))]
142 CompactUpdateVoteState(VoteStateUpdate),
143
144 CompactUpdateVoteStateSwitch(
150 #[cfg_attr(feature = "serde", serde(with = "serde_compact_vote_state_update"))]
151 VoteStateUpdate,
152 Hash,
153 ),
154
155 #[cfg_attr(feature = "serde", serde(with = "serde_tower_sync"))]
161 TowerSync(TowerSync),
162
163 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 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 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 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: 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, 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, 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}