solana_runtime/
vote_parser.rs1use {
2 crate::vote_transaction::VoteTransaction,
3 solana_sdk::{
4 hash::Hash,
5 program_utils::limited_deserialize,
6 pubkey::Pubkey,
7 signature::Signature,
8 transaction::{SanitizedTransaction, Transaction},
9 },
10 solana_vote_program::vote_instruction::VoteInstruction,
11};
12
13pub type ParsedVote = (Pubkey, VoteTransaction, Option<Hash>, Signature);
14
15pub(crate) fn is_simple_vote_transaction(transaction: &SanitizedTransaction) -> bool {
17 if transaction.message().instructions().len() == 1 {
18 let (program_pubkey, instruction) = transaction
19 .message()
20 .program_instructions_iter()
21 .next()
22 .unwrap();
23 if program_pubkey == &solana_vote_program::id() {
24 if let Ok(vote_instruction) = limited_deserialize::<VoteInstruction>(&instruction.data)
25 {
26 return matches!(
27 vote_instruction,
28 VoteInstruction::Vote(_)
29 | VoteInstruction::VoteSwitch(_, _)
30 | VoteInstruction::UpdateVoteState(_)
31 | VoteInstruction::UpdateVoteStateSwitch(_, _)
32 | VoteInstruction::CompactUpdateVoteState(_)
33 | VoteInstruction::CompactUpdateVoteStateSwitch(..)
34 );
35 }
36 }
37 }
38 false
39}
40
41pub fn parse_sanitized_vote_transaction(tx: &SanitizedTransaction) -> Option<ParsedVote> {
43 let message = tx.message();
45 let (program_id, first_instruction) = message.program_instructions_iter().next()?;
46 if !solana_vote_program::check_id(program_id) {
47 return None;
48 }
49 let first_account = usize::from(*first_instruction.accounts.first()?);
50 let key = message.account_keys().get(first_account)?;
51 let (vote, switch_proof_hash) = parse_vote_instruction_data(&first_instruction.data)?;
52 let signature = tx.signatures().get(0).cloned().unwrap_or_default();
53 Some((*key, vote, switch_proof_hash, signature))
54}
55
56pub fn parse_vote_transaction(tx: &Transaction) -> Option<ParsedVote> {
58 let message = tx.message();
60 let first_instruction = message.instructions.first()?;
61 let program_id_index = usize::from(first_instruction.program_id_index);
62 let program_id = message.account_keys.get(program_id_index)?;
63 if !solana_vote_program::check_id(program_id) {
64 return None;
65 }
66 let first_account = usize::from(*first_instruction.accounts.first()?);
67 let key = message.account_keys.get(first_account)?;
68 let (vote, switch_proof_hash) = parse_vote_instruction_data(&first_instruction.data)?;
69 let signature = tx.signatures.get(0).cloned().unwrap_or_default();
70 Some((*key, vote, switch_proof_hash, signature))
71}
72
73fn parse_vote_instruction_data(
74 vote_instruction_data: &[u8],
75) -> Option<(VoteTransaction, Option<Hash>)> {
76 match limited_deserialize(vote_instruction_data).ok()? {
77 VoteInstruction::Vote(vote) => Some((VoteTransaction::from(vote), None)),
78 VoteInstruction::VoteSwitch(vote, hash) => Some((VoteTransaction::from(vote), Some(hash))),
79 VoteInstruction::UpdateVoteState(vote_state_update) => {
80 Some((VoteTransaction::from(vote_state_update), None))
81 }
82 VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash) => {
83 Some((VoteTransaction::from(vote_state_update), Some(hash)))
84 }
85 VoteInstruction::CompactUpdateVoteState(vote_state_update) => {
86 Some((VoteTransaction::from(vote_state_update), None))
87 }
88 VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, hash) => {
89 Some((VoteTransaction::from(vote_state_update), Some(hash)))
90 }
91 VoteInstruction::Authorize(_, _)
92 | VoteInstruction::AuthorizeChecked(_)
93 | VoteInstruction::AuthorizeWithSeed(_)
94 | VoteInstruction::AuthorizeCheckedWithSeed(_)
95 | VoteInstruction::InitializeAccount(_)
96 | VoteInstruction::UpdateCommission(_)
97 | VoteInstruction::UpdateValidatorIdentity
98 | VoteInstruction::Withdraw(_) => None,
99 }
100}
101
102#[cfg(test)]
103mod test {
104 use {
105 super::*,
106 solana_sdk::{
107 hash::hash,
108 signature::{Keypair, Signer},
109 },
110 solana_vote_program::{
111 vote_instruction, vote_state::Vote, vote_transaction::new_vote_transaction,
112 },
113 };
114
115 fn run_test_parse_vote_transaction(input_hash: Option<Hash>) {
116 let node_keypair = Keypair::new();
117 let vote_keypair = Keypair::new();
118 let auth_voter_keypair = Keypair::new();
119 let bank_hash = Hash::default();
120 let vote_tx = new_vote_transaction(
121 vec![42],
122 bank_hash,
123 Hash::default(),
124 &node_keypair,
125 &vote_keypair,
126 &auth_voter_keypair,
127 input_hash,
128 );
129 let (key, vote, hash, signature) = parse_vote_transaction(&vote_tx).unwrap();
130 assert_eq!(hash, input_hash);
131 assert_eq!(vote, VoteTransaction::from(Vote::new(vec![42], bank_hash)));
132 assert_eq!(key, vote_keypair.pubkey());
133 assert_eq!(signature, vote_tx.signatures[0]);
134
135 let mut vote_ix = vote_instruction::vote(
137 &vote_keypair.pubkey(),
138 &auth_voter_keypair.pubkey(),
139 Vote::new(vec![1, 2], Hash::default()),
140 );
141 vote_ix.program_id = Pubkey::default();
142 let vote_tx = Transaction::new_with_payer(&[vote_ix], Some(&node_keypair.pubkey()));
143 assert!(parse_vote_transaction(&vote_tx).is_none());
144 }
145
146 #[test]
147 fn test_parse_vote_transaction() {
148 run_test_parse_vote_transaction(None);
149 run_test_parse_vote_transaction(Some(hash(&[42u8])));
150 }
151}