solana_transaction_status/
parse_vote.rs

1use {
2    crate::parse_instruction::{
3        check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum,
4    },
5    bincode::deserialize,
6    serde_json::json,
7    solana_message::{compiled_instruction::CompiledInstruction, AccountKeys},
8    solana_program::vote::instruction::VoteInstruction,
9};
10
11pub fn parse_vote(
12    instruction: &CompiledInstruction,
13    account_keys: &AccountKeys,
14) -> Result<ParsedInstructionEnum, ParseInstructionError> {
15    let vote_instruction: VoteInstruction = deserialize(&instruction.data)
16        .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::Vote))?;
17    match instruction.accounts.iter().max() {
18        Some(index) if (*index as usize) < account_keys.len() => {}
19        _ => {
20            // Runtime should prevent this from ever happening
21            return Err(ParseInstructionError::InstructionKeyMismatch(
22                ParsableProgram::Vote,
23            ));
24        }
25    }
26    match vote_instruction {
27        VoteInstruction::InitializeAccount(vote_init) => {
28            check_num_vote_accounts(&instruction.accounts, 4)?;
29            Ok(ParsedInstructionEnum {
30                instruction_type: "initialize".to_string(),
31                info: json!({
32                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
33                    "rentSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
34                    "clockSysvar": account_keys[instruction.accounts[2] as usize].to_string(),
35                    "node": account_keys[instruction.accounts[3] as usize].to_string(),
36                    "authorizedVoter": vote_init.authorized_voter.to_string(),
37                    "authorizedWithdrawer": vote_init.authorized_withdrawer.to_string(),
38                    "commission": vote_init.commission,
39                }),
40            })
41        }
42        VoteInstruction::Authorize(new_authorized, authority_type) => {
43            check_num_vote_accounts(&instruction.accounts, 3)?;
44            Ok(ParsedInstructionEnum {
45                instruction_type: "authorize".to_string(),
46                info: json!({
47                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
48                    "clockSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
49                    "authority": account_keys[instruction.accounts[2] as usize].to_string(),
50                    "newAuthority": new_authorized.to_string(),
51                    "authorityType": authority_type,
52                }),
53            })
54        }
55        VoteInstruction::AuthorizeWithSeed(args) => {
56            check_num_vote_accounts(&instruction.accounts, 3)?;
57            Ok(ParsedInstructionEnum {
58                instruction_type: "authorizeWithSeed".to_string(),
59                info: json!({
60                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
61                    "clockSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
62                    "authorityBaseKey": account_keys[instruction.accounts[2] as usize].to_string(),
63                    "authorityOwner": args.current_authority_derived_key_owner.to_string(),
64                    "authoritySeed": args.current_authority_derived_key_seed,
65                    "newAuthority": args.new_authority.to_string(),
66                    "authorityType": args.authorization_type,
67                }),
68            })
69        }
70        VoteInstruction::AuthorizeCheckedWithSeed(args) => {
71            check_num_vote_accounts(&instruction.accounts, 4)?;
72            Ok(ParsedInstructionEnum {
73                instruction_type: "authorizeCheckedWithSeed".to_string(),
74                info: json!({
75                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
76                    "clockSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
77                    "authorityBaseKey": account_keys[instruction.accounts[2] as usize].to_string(),
78                    "authorityOwner": args.current_authority_derived_key_owner.to_string(),
79                    "authoritySeed": args.current_authority_derived_key_seed,
80                    "newAuthority": account_keys[instruction.accounts[3] as usize].to_string(),
81                    "authorityType": args.authorization_type,
82                }),
83            })
84        }
85        VoteInstruction::Vote(vote) => {
86            check_num_vote_accounts(&instruction.accounts, 4)?;
87            let vote = json!({
88                "slots": vote.slots,
89                "hash": vote.hash.to_string(),
90                "timestamp": vote.timestamp,
91            });
92            Ok(ParsedInstructionEnum {
93                instruction_type: "vote".to_string(),
94                info: json!({
95                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
96                    "slotHashesSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
97                    "clockSysvar": account_keys[instruction.accounts[2] as usize].to_string(),
98                    "voteAuthority": account_keys[instruction.accounts[3] as usize].to_string(),
99                    "vote": vote,
100                }),
101            })
102        }
103        VoteInstruction::UpdateVoteState(vote_state_update) => {
104            check_num_vote_accounts(&instruction.accounts, 2)?;
105            let vote_state_update = json!({
106                "lockouts": vote_state_update.lockouts,
107                "root": vote_state_update.root,
108                "hash": vote_state_update.hash.to_string(),
109                "timestamp": vote_state_update.timestamp,
110            });
111            Ok(ParsedInstructionEnum {
112                instruction_type: "updatevotestate".to_string(),
113                info: json!({
114                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
115                    "voteAuthority": account_keys[instruction.accounts[1] as usize].to_string(),
116                    "voteStateUpdate": vote_state_update,
117                }),
118            })
119        }
120        VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash) => {
121            check_num_vote_accounts(&instruction.accounts, 2)?;
122            let vote_state_update = json!({
123                "lockouts": vote_state_update.lockouts,
124                "root": vote_state_update.root,
125                "hash": vote_state_update.hash.to_string(),
126                "timestamp": vote_state_update.timestamp,
127            });
128            Ok(ParsedInstructionEnum {
129                instruction_type: "updatevotestateswitch".to_string(),
130                info: json!({
131                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
132                    "voteAuthority": account_keys[instruction.accounts[1] as usize].to_string(),
133                    "voteStateUpdate": vote_state_update,
134                    "hash": hash.to_string(),
135                }),
136            })
137        }
138        VoteInstruction::CompactUpdateVoteState(vote_state_update) => {
139            check_num_vote_accounts(&instruction.accounts, 2)?;
140            let vote_state_update = json!({
141                "lockouts": vote_state_update.lockouts,
142                "root": vote_state_update.root,
143                "hash": vote_state_update.hash.to_string(),
144                "timestamp": vote_state_update.timestamp,
145            });
146            Ok(ParsedInstructionEnum {
147                instruction_type: "compactupdatevotestate".to_string(),
148                info: json!({
149                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
150                    "voteAuthority": account_keys[instruction.accounts[1] as usize].to_string(),
151                    "voteStateUpdate": vote_state_update,
152                }),
153            })
154        }
155        VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, hash) => {
156            check_num_vote_accounts(&instruction.accounts, 2)?;
157            let vote_state_update = json!({
158                "lockouts": vote_state_update.lockouts,
159                "root": vote_state_update.root,
160                "hash": vote_state_update.hash.to_string(),
161                "timestamp": vote_state_update.timestamp,
162            });
163            Ok(ParsedInstructionEnum {
164                instruction_type: "compactupdatevotestateswitch".to_string(),
165                info: json!({
166                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
167                    "voteAuthority": account_keys[instruction.accounts[1] as usize].to_string(),
168                    "voteStateUpdate": vote_state_update,
169                    "hash": hash.to_string(),
170                }),
171            })
172        }
173        VoteInstruction::TowerSync(tower_sync) => {
174            check_num_vote_accounts(&instruction.accounts, 2)?;
175            let tower_sync = json!({
176                "lockouts": tower_sync.lockouts,
177                "root": tower_sync.root,
178                "hash": tower_sync.hash.to_string(),
179                "timestamp": tower_sync.timestamp,
180                "blockId": tower_sync.block_id.to_string(),
181            });
182            Ok(ParsedInstructionEnum {
183                instruction_type: "towersync".to_string(),
184                info: json!({
185                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
186                    "voteAuthority": account_keys[instruction.accounts[1] as usize].to_string(),
187                    "towerSync": tower_sync,
188                }),
189            })
190        }
191        VoteInstruction::TowerSyncSwitch(tower_sync, hash) => {
192            check_num_vote_accounts(&instruction.accounts, 2)?;
193            let tower_sync = json!({
194                "lockouts": tower_sync.lockouts,
195                "root": tower_sync.root,
196                "hash": tower_sync.hash.to_string(),
197                "timestamp": tower_sync.timestamp,
198                "blockId": tower_sync.block_id.to_string(),
199            });
200            Ok(ParsedInstructionEnum {
201                instruction_type: "towersyncswitch".to_string(),
202                info: json!({
203                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
204                    "voteAuthority": account_keys[instruction.accounts[1] as usize].to_string(),
205                    "towerSync": tower_sync,
206                    "hash": hash.to_string(),
207                }),
208            })
209        }
210        VoteInstruction::Withdraw(lamports) => {
211            check_num_vote_accounts(&instruction.accounts, 3)?;
212            Ok(ParsedInstructionEnum {
213                instruction_type: "withdraw".to_string(),
214                info: json!({
215                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
216                    "destination": account_keys[instruction.accounts[1] as usize].to_string(),
217                    "withdrawAuthority": account_keys[instruction.accounts[2] as usize].to_string(),
218                    "lamports": lamports,
219                }),
220            })
221        }
222        VoteInstruction::UpdateValidatorIdentity => {
223            check_num_vote_accounts(&instruction.accounts, 3)?;
224            Ok(ParsedInstructionEnum {
225                instruction_type: "updateValidatorIdentity".to_string(),
226                info: json!({
227                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
228                    "newValidatorIdentity": account_keys[instruction.accounts[1] as usize].to_string(),
229                    "withdrawAuthority": account_keys[instruction.accounts[2] as usize].to_string(),
230                }),
231            })
232        }
233        VoteInstruction::UpdateCommission(commission) => {
234            check_num_vote_accounts(&instruction.accounts, 2)?;
235            Ok(ParsedInstructionEnum {
236                instruction_type: "updateCommission".to_string(),
237                info: json!({
238                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
239                    "withdrawAuthority": account_keys[instruction.accounts[1] as usize].to_string(),
240                    "commission": commission,
241                }),
242            })
243        }
244        VoteInstruction::VoteSwitch(vote, hash) => {
245            check_num_vote_accounts(&instruction.accounts, 4)?;
246            let vote = json!({
247                "slots": vote.slots,
248                "hash": vote.hash.to_string(),
249                "timestamp": vote.timestamp,
250            });
251            Ok(ParsedInstructionEnum {
252                instruction_type: "voteSwitch".to_string(),
253                info: json!({
254                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
255                    "slotHashesSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
256                    "clockSysvar": account_keys[instruction.accounts[2] as usize].to_string(),
257                    "voteAuthority": account_keys[instruction.accounts[3] as usize].to_string(),
258                    "vote": vote,
259                    "hash": hash.to_string(),
260                }),
261            })
262        }
263        VoteInstruction::AuthorizeChecked(authority_type) => {
264            check_num_vote_accounts(&instruction.accounts, 4)?;
265            Ok(ParsedInstructionEnum {
266                instruction_type: "authorizeChecked".to_string(),
267                info: json!({
268                    "voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
269                    "clockSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
270                    "authority": account_keys[instruction.accounts[2] as usize].to_string(),
271                    "newAuthority": account_keys[instruction.accounts[3] as usize].to_string(),
272                    "authorityType": authority_type,
273                }),
274            })
275        }
276    }
277}
278
279fn check_num_vote_accounts(accounts: &[u8], num: usize) -> Result<(), ParseInstructionError> {
280    check_num_accounts(accounts, num, ParsableProgram::Vote)
281}
282
283#[cfg(test)]
284mod test {
285    use {
286        super::*,
287        solana_hash::Hash,
288        solana_message::Message,
289        solana_program::vote::{
290            instruction as vote_instruction,
291            state::{TowerSync, Vote, VoteAuthorize, VoteInit, VoteStateUpdate, VoteStateVersions},
292        },
293        solana_pubkey::Pubkey,
294        solana_sdk_ids::sysvar,
295    };
296
297    #[test]
298    fn test_parse_vote_initialize_ix() {
299        let lamports = 55;
300
301        let commission = 10;
302        let node_pubkey = Pubkey::new_unique();
303        let vote_pubkey = Pubkey::new_unique();
304        let authorized_voter = Pubkey::new_unique();
305        let authorized_withdrawer = Pubkey::new_unique();
306        let vote_init = VoteInit {
307            node_pubkey,
308            authorized_voter,
309            authorized_withdrawer,
310            commission,
311        };
312
313        let instructions = vote_instruction::create_account_with_config(
314            &Pubkey::new_unique(),
315            &vote_pubkey,
316            &vote_init,
317            lamports,
318            vote_instruction::CreateVoteAccountConfig {
319                space: VoteStateVersions::vote_state_size_of(true) as u64,
320                ..vote_instruction::CreateVoteAccountConfig::default()
321            },
322        );
323        let mut message = Message::new(&instructions, None);
324        assert_eq!(
325            parse_vote(
326                &message.instructions[1],
327                &AccountKeys::new(&message.account_keys, None)
328            )
329            .unwrap(),
330            ParsedInstructionEnum {
331                instruction_type: "initialize".to_string(),
332                info: json!({
333                    "voteAccount": vote_pubkey.to_string(),
334                    "rentSysvar": sysvar::rent::ID.to_string(),
335                    "clockSysvar": sysvar::clock::ID.to_string(),
336                    "node": node_pubkey.to_string(),
337                    "authorizedVoter": authorized_voter.to_string(),
338                    "authorizedWithdrawer": authorized_withdrawer.to_string(),
339                    "commission": commission,
340                }),
341            }
342        );
343        assert!(parse_vote(
344            &message.instructions[1],
345            &AccountKeys::new(&message.account_keys[0..3], None)
346        )
347        .is_err());
348        let keys = message.account_keys.clone();
349        message.instructions[0].accounts.pop();
350        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
351    }
352
353    #[test]
354    fn test_parse_vote_authorize_ix() {
355        let vote_pubkey = Pubkey::new_unique();
356        let authorized_pubkey = Pubkey::new_unique();
357        let new_authorized_pubkey = Pubkey::new_unique();
358        let authority_type = VoteAuthorize::Voter;
359        let instruction = vote_instruction::authorize(
360            &vote_pubkey,
361            &authorized_pubkey,
362            &new_authorized_pubkey,
363            authority_type,
364        );
365        let mut message = Message::new(&[instruction], None);
366        assert_eq!(
367            parse_vote(
368                &message.instructions[0],
369                &AccountKeys::new(&message.account_keys, None)
370            )
371            .unwrap(),
372            ParsedInstructionEnum {
373                instruction_type: "authorize".to_string(),
374                info: json!({
375                    "voteAccount": vote_pubkey.to_string(),
376                    "clockSysvar": sysvar::clock::ID.to_string(),
377                    "authority": authorized_pubkey.to_string(),
378                    "newAuthority": new_authorized_pubkey.to_string(),
379                    "authorityType": authority_type,
380                }),
381            }
382        );
383        assert!(parse_vote(
384            &message.instructions[0],
385            &AccountKeys::new(&message.account_keys[0..2], None)
386        )
387        .is_err());
388        let keys = message.account_keys.clone();
389        message.instructions[0].accounts.pop();
390        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
391    }
392
393    #[test]
394    fn test_parse_vote_authorize_with_seed_ix() {
395        let vote_pubkey = Pubkey::new_unique();
396        let authorized_base_key = Pubkey::new_unique();
397        let new_authorized_pubkey = Pubkey::new_unique();
398        let authority_type = VoteAuthorize::Voter;
399        let current_authority_owner = Pubkey::new_unique();
400        let current_authority_seed = "AUTHORITY_SEED";
401        let instruction = vote_instruction::authorize_with_seed(
402            &vote_pubkey,
403            &authorized_base_key,
404            &current_authority_owner,
405            current_authority_seed,
406            &new_authorized_pubkey,
407            authority_type,
408        );
409        let message = Message::new(&[instruction], None);
410        assert_eq!(
411            parse_vote(
412                &message.instructions[0],
413                &AccountKeys::new(&message.account_keys, None)
414            )
415            .unwrap(),
416            ParsedInstructionEnum {
417                instruction_type: "authorizeWithSeed".to_string(),
418                info: json!({
419                    "voteAccount": vote_pubkey.to_string(),
420                    "clockSysvar": sysvar::clock::ID.to_string(),
421                    "authorityBaseKey": authorized_base_key.to_string(),
422                    "authorityOwner": current_authority_owner.to_string(),
423                    "authoritySeed": current_authority_seed,
424                    "newAuthority": new_authorized_pubkey.to_string(),
425                    "authorityType": authority_type,
426                }),
427            }
428        );
429        assert!(parse_vote(
430            &message.instructions[0],
431            &AccountKeys::new(&message.account_keys[0..2], None)
432        )
433        .is_err());
434    }
435
436    #[test]
437    fn test_parse_vote_authorize_with_seed_checked_ix() {
438        let vote_pubkey = Pubkey::new_unique();
439        let authorized_base_key = Pubkey::new_unique();
440        let new_authorized_pubkey = Pubkey::new_unique();
441        let authority_type = VoteAuthorize::Voter;
442        let current_authority_owner = Pubkey::new_unique();
443        let current_authority_seed = "AUTHORITY_SEED";
444        let instruction = vote_instruction::authorize_checked_with_seed(
445            &vote_pubkey,
446            &authorized_base_key,
447            &current_authority_owner,
448            current_authority_seed,
449            &new_authorized_pubkey,
450            authority_type,
451        );
452        let message = Message::new(&[instruction], None);
453        assert_eq!(
454            parse_vote(
455                &message.instructions[0],
456                &AccountKeys::new(&message.account_keys, None)
457            )
458            .unwrap(),
459            ParsedInstructionEnum {
460                instruction_type: "authorizeCheckedWithSeed".to_string(),
461                info: json!({
462                    "voteAccount": vote_pubkey.to_string(),
463                    "clockSysvar": sysvar::clock::ID.to_string(),
464                    "authorityBaseKey": authorized_base_key.to_string(),
465                    "authorityOwner": current_authority_owner.to_string(),
466                    "authoritySeed": current_authority_seed,
467                    "newAuthority": new_authorized_pubkey.to_string(),
468                    "authorityType": authority_type,
469                }),
470            }
471        );
472        assert!(parse_vote(
473            &message.instructions[0],
474            &AccountKeys::new(&message.account_keys[0..3], None)
475        )
476        .is_err());
477    }
478
479    #[test]
480    fn test_parse_vote_ix() {
481        let hash = Hash::new_from_array([1; 32]);
482        let vote = Vote {
483            slots: vec![1, 2, 4],
484            hash,
485            timestamp: Some(1_234_567_890),
486        };
487
488        let vote_pubkey = Pubkey::new_unique();
489        let authorized_voter_pubkey = Pubkey::new_unique();
490        let instruction = vote_instruction::vote(&vote_pubkey, &authorized_voter_pubkey, vote);
491        let mut message = Message::new(&[instruction], None);
492        assert_eq!(
493            parse_vote(
494                &message.instructions[0],
495                &AccountKeys::new(&message.account_keys, None)
496            )
497            .unwrap(),
498            ParsedInstructionEnum {
499                instruction_type: "vote".to_string(),
500                info: json!({
501                    "voteAccount": vote_pubkey.to_string(),
502                    "slotHashesSysvar": sysvar::slot_hashes::ID.to_string(),
503                    "clockSysvar": sysvar::clock::ID.to_string(),
504                    "voteAuthority": authorized_voter_pubkey.to_string(),
505                    "vote": {
506                        "slots": [1, 2, 4],
507                        "hash": hash.to_string(),
508                        "timestamp": 1_234_567_890,
509                    },
510                }),
511            }
512        );
513        assert!(parse_vote(
514            &message.instructions[0],
515            &AccountKeys::new(&message.account_keys[0..3], None)
516        )
517        .is_err());
518        let keys = message.account_keys.clone();
519        message.instructions[0].accounts.pop();
520        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
521    }
522
523    #[test]
524    fn test_parse_vote_withdraw_ix() {
525        let lamports = 55;
526        let vote_pubkey = Pubkey::new_unique();
527        let authorized_withdrawer_pubkey = Pubkey::new_unique();
528        let to_pubkey = Pubkey::new_unique();
529        let instruction = vote_instruction::withdraw(
530            &vote_pubkey,
531            &authorized_withdrawer_pubkey,
532            lamports,
533            &to_pubkey,
534        );
535        let mut message = Message::new(&[instruction], None);
536        assert_eq!(
537            parse_vote(
538                &message.instructions[0],
539                &AccountKeys::new(&message.account_keys, None)
540            )
541            .unwrap(),
542            ParsedInstructionEnum {
543                instruction_type: "withdraw".to_string(),
544                info: json!({
545                    "voteAccount": vote_pubkey.to_string(),
546                    "destination": to_pubkey.to_string(),
547                    "withdrawAuthority": authorized_withdrawer_pubkey.to_string(),
548                    "lamports": lamports,
549                }),
550            }
551        );
552        assert!(parse_vote(
553            &message.instructions[0],
554            &AccountKeys::new(&message.account_keys[0..2], None)
555        )
556        .is_err());
557        let keys = message.account_keys.clone();
558        message.instructions[0].accounts.pop();
559        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
560    }
561
562    #[test]
563    fn test_parse_vote_update_validator_identity_ix() {
564        let vote_pubkey = Pubkey::new_unique();
565        let authorized_withdrawer_pubkey = Pubkey::new_unique();
566        let node_pubkey = Pubkey::new_unique();
567        let instruction = vote_instruction::update_validator_identity(
568            &vote_pubkey,
569            &authorized_withdrawer_pubkey,
570            &node_pubkey,
571        );
572        let mut message = Message::new(&[instruction], None);
573        assert_eq!(
574            parse_vote(
575                &message.instructions[0],
576                &AccountKeys::new(&message.account_keys, None)
577            )
578            .unwrap(),
579            ParsedInstructionEnum {
580                instruction_type: "updateValidatorIdentity".to_string(),
581                info: json!({
582                    "voteAccount": vote_pubkey.to_string(),
583                    "newValidatorIdentity": node_pubkey.to_string(),
584                    "withdrawAuthority": authorized_withdrawer_pubkey.to_string(),
585                }),
586            }
587        );
588        assert!(parse_vote(
589            &message.instructions[0],
590            &AccountKeys::new(&message.account_keys[0..2], None)
591        )
592        .is_err());
593        let keys = message.account_keys.clone();
594        message.instructions[0].accounts.pop();
595        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
596    }
597
598    #[test]
599    fn test_parse_vote_update_commission_ix() {
600        let commission = 10;
601        let vote_pubkey = Pubkey::new_unique();
602        let authorized_withdrawer_pubkey = Pubkey::new_unique();
603        let instruction = vote_instruction::update_commission(
604            &vote_pubkey,
605            &authorized_withdrawer_pubkey,
606            commission,
607        );
608        let mut message = Message::new(&[instruction], None);
609        assert_eq!(
610            parse_vote(
611                &message.instructions[0],
612                &AccountKeys::new(&message.account_keys, None)
613            )
614            .unwrap(),
615            ParsedInstructionEnum {
616                instruction_type: "updateCommission".to_string(),
617                info: json!({
618                    "voteAccount": vote_pubkey.to_string(),
619                    "withdrawAuthority": authorized_withdrawer_pubkey.to_string(),
620                    "commission": commission,
621                }),
622            }
623        );
624        assert!(parse_vote(
625            &message.instructions[0],
626            &AccountKeys::new(&message.account_keys[0..1], None)
627        )
628        .is_err());
629        let keys = message.account_keys.clone();
630        message.instructions[0].accounts.pop();
631        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
632    }
633
634    #[test]
635    fn test_parse_vote_switch_ix() {
636        let hash = Hash::new_from_array([1; 32]);
637        let vote = Vote {
638            slots: vec![1, 2, 4],
639            hash,
640            timestamp: Some(1_234_567_890),
641        };
642
643        let vote_pubkey = Pubkey::new_unique();
644        let authorized_voter_pubkey = Pubkey::new_unique();
645        let proof_hash = Hash::new_from_array([2; 32]);
646        let instruction =
647            vote_instruction::vote_switch(&vote_pubkey, &authorized_voter_pubkey, vote, proof_hash);
648        let mut message = Message::new(&[instruction], None);
649        assert_eq!(
650            parse_vote(
651                &message.instructions[0],
652                &AccountKeys::new(&message.account_keys, None)
653            )
654            .unwrap(),
655            ParsedInstructionEnum {
656                instruction_type: "voteSwitch".to_string(),
657                info: json!({
658                    "voteAccount": vote_pubkey.to_string(),
659                    "slotHashesSysvar": sysvar::slot_hashes::ID.to_string(),
660                    "clockSysvar": sysvar::clock::ID.to_string(),
661                    "voteAuthority": authorized_voter_pubkey.to_string(),
662                    "vote": {
663                        "slots": [1, 2, 4],
664                        "hash": hash.to_string(),
665                        "timestamp": 1_234_567_890,
666                    },
667                    "hash": proof_hash.to_string(),
668                }),
669            }
670        );
671        assert!(parse_vote(
672            &message.instructions[0],
673            &AccountKeys::new(&message.account_keys[0..3], None)
674        )
675        .is_err());
676        let keys = message.account_keys.clone();
677        message.instructions[0].accounts.pop();
678        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
679    }
680
681    #[test]
682    fn test_parse_vote_authorized_checked_ix() {
683        let vote_pubkey = Pubkey::new_unique();
684        let authorized_pubkey = Pubkey::new_unique();
685        let new_authorized_pubkey = Pubkey::new_unique();
686        let authority_type = VoteAuthorize::Voter;
687        let instruction = vote_instruction::authorize_checked(
688            &vote_pubkey,
689            &authorized_pubkey,
690            &new_authorized_pubkey,
691            authority_type,
692        );
693        let mut message = Message::new(&[instruction], None);
694        assert_eq!(
695            parse_vote(
696                &message.instructions[0],
697                &AccountKeys::new(&message.account_keys, None)
698            )
699            .unwrap(),
700            ParsedInstructionEnum {
701                instruction_type: "authorizeChecked".to_string(),
702                info: json!({
703                    "voteAccount": vote_pubkey.to_string(),
704                    "clockSysvar": sysvar::clock::ID.to_string(),
705                    "authority": authorized_pubkey.to_string(),
706                    "newAuthority": new_authorized_pubkey.to_string(),
707                    "authorityType": authority_type,
708                }),
709            }
710        );
711        assert!(parse_vote(
712            &message.instructions[0],
713            &AccountKeys::new(&message.account_keys[0..3], None)
714        )
715        .is_err());
716        let keys = message.account_keys.clone();
717        message.instructions[0].accounts.pop();
718        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
719    }
720
721    #[test]
722    fn test_parse_vote_state_update_ix() {
723        let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
724
725        let vote_pubkey = Pubkey::new_unique();
726        let authorized_voter_pubkey = Pubkey::new_unique();
727        let instruction = vote_instruction::update_vote_state(
728            &vote_pubkey,
729            &authorized_voter_pubkey,
730            vote_state_update.clone(),
731        );
732        let mut message = Message::new(&[instruction], None);
733        assert_eq!(
734            parse_vote(
735                &message.instructions[0],
736                &AccountKeys::new(&message.account_keys, None)
737            )
738            .unwrap(),
739            ParsedInstructionEnum {
740                instruction_type: "updatevotestate".to_string(),
741                info: json!({
742                    "voteAccount": vote_pubkey.to_string(),
743                    "voteAuthority": authorized_voter_pubkey.to_string(),
744                    "voteStateUpdate": {
745                        "lockouts": vote_state_update.lockouts,
746                        "root": None::<u64>,
747                        "hash": Hash::default().to_string(),
748                        "timestamp": None::<u64>,
749                    },
750                }),
751            }
752        );
753        assert!(parse_vote(
754            &message.instructions[0],
755            &AccountKeys::new(&message.account_keys[0..1], None)
756        )
757        .is_err());
758        let keys = message.account_keys.clone();
759        message.instructions[0].accounts.pop();
760        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
761    }
762
763    #[test]
764    fn test_parse_vote_state_update_switch_ix() {
765        let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
766
767        let vote_pubkey = Pubkey::new_unique();
768        let authorized_voter_pubkey = Pubkey::new_unique();
769        let proof_hash = Hash::new_from_array([2; 32]);
770        let instruction = vote_instruction::update_vote_state_switch(
771            &vote_pubkey,
772            &authorized_voter_pubkey,
773            vote_state_update.clone(),
774            proof_hash,
775        );
776        let mut message = Message::new(&[instruction], None);
777        assert_eq!(
778            parse_vote(
779                &message.instructions[0],
780                &AccountKeys::new(&message.account_keys, None)
781            )
782            .unwrap(),
783            ParsedInstructionEnum {
784                instruction_type: "updatevotestateswitch".to_string(),
785                info: json!({
786                    "voteAccount": vote_pubkey.to_string(),
787                    "voteAuthority": authorized_voter_pubkey.to_string(),
788                    "voteStateUpdate": {
789                        "lockouts": vote_state_update.lockouts,
790                        "root": None::<u64>,
791                        "hash": Hash::default().to_string(),
792                        "timestamp": None::<u64>,
793                    },
794                    "hash": proof_hash.to_string(),
795                }),
796            }
797        );
798        assert!(parse_vote(
799            &message.instructions[0],
800            &AccountKeys::new(&message.account_keys[0..1], None)
801        )
802        .is_err());
803        let keys = message.account_keys.clone();
804        message.instructions[0].accounts.pop();
805        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
806    }
807
808    #[test]
809    fn test_parse_compact_vote_state_update_ix() {
810        let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
811        let compact_vote_state_update = vote_state_update.clone();
812
813        let vote_pubkey = Pubkey::new_unique();
814        let authorized_voter_pubkey = Pubkey::new_unique();
815        let instruction = vote_instruction::compact_update_vote_state(
816            &vote_pubkey,
817            &authorized_voter_pubkey,
818            compact_vote_state_update,
819        );
820        let mut message = Message::new(&[instruction], None);
821        assert_eq!(
822            parse_vote(
823                &message.instructions[0],
824                &AccountKeys::new(&message.account_keys, None)
825            )
826            .unwrap(),
827            ParsedInstructionEnum {
828                instruction_type: "compactupdatevotestate".to_string(),
829                info: json!({
830                    "voteAccount": vote_pubkey.to_string(),
831                    "voteAuthority": authorized_voter_pubkey.to_string(),
832                    "voteStateUpdate": {
833                        "lockouts": vote_state_update.lockouts,
834                        "root": None::<u64>,
835                        "hash": Hash::default().to_string(),
836                        "timestamp": None::<u64>,
837                    },
838                }),
839            }
840        );
841        assert!(parse_vote(
842            &message.instructions[0],
843            &AccountKeys::new(&message.account_keys[0..1], None)
844        )
845        .is_err());
846        let keys = message.account_keys.clone();
847        message.instructions[0].accounts.pop();
848        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
849    }
850
851    #[test]
852    fn test_parse_compact_vote_state_update_switch_ix() {
853        let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
854        let compact_vote_state_update = vote_state_update.clone();
855
856        let vote_pubkey = Pubkey::new_unique();
857        let authorized_voter_pubkey = Pubkey::new_unique();
858        let proof_hash = Hash::new_from_array([2; 32]);
859        let instruction = vote_instruction::compact_update_vote_state_switch(
860            &vote_pubkey,
861            &authorized_voter_pubkey,
862            compact_vote_state_update,
863            proof_hash,
864        );
865        let mut message = Message::new(&[instruction], None);
866        assert_eq!(
867            parse_vote(
868                &message.instructions[0],
869                &AccountKeys::new(&message.account_keys, None)
870            )
871            .unwrap(),
872            ParsedInstructionEnum {
873                instruction_type: "compactupdatevotestateswitch".to_string(),
874                info: json!({
875                    "voteAccount": vote_pubkey.to_string(),
876                    "voteAuthority": authorized_voter_pubkey.to_string(),
877                    "voteStateUpdate": {
878                        "lockouts": vote_state_update.lockouts,
879                        "root": None::<u64>,
880                        "hash": Hash::default().to_string(),
881                        "timestamp": None::<u64>,
882                    },
883                    "hash": proof_hash.to_string(),
884                }),
885            }
886        );
887        assert!(parse_vote(
888            &message.instructions[0],
889            &AccountKeys::new(&message.account_keys[0..1], None)
890        )
891        .is_err());
892        let keys = message.account_keys.clone();
893        message.instructions[0].accounts.pop();
894        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
895    }
896
897    #[test]
898    fn test_parse_tower_sync_ix() {
899        let tower_sync = TowerSync::from(vec![(0, 3), (1, 2), (2, 1)]);
900
901        let vote_pubkey = Pubkey::new_unique();
902        let authorized_voter_pubkey = Pubkey::new_unique();
903        let instruction = vote_instruction::tower_sync(
904            &vote_pubkey,
905            &authorized_voter_pubkey,
906            tower_sync.clone(),
907        );
908        let mut message = Message::new(&[instruction], None);
909        assert_eq!(
910            parse_vote(
911                &message.instructions[0],
912                &AccountKeys::new(&message.account_keys, None)
913            )
914            .unwrap(),
915            ParsedInstructionEnum {
916                instruction_type: "towersync".to_string(),
917                info: json!({
918                    "voteAccount": vote_pubkey.to_string(),
919                    "voteAuthority": authorized_voter_pubkey.to_string(),
920                    "towerSync": {
921                        "lockouts": tower_sync.lockouts,
922                        "root": None::<u64>,
923                        "hash": Hash::default().to_string(),
924                        "timestamp": None::<u64>,
925                        "blockId": Hash::default().to_string(),
926                    },
927                }),
928            }
929        );
930        assert!(parse_vote(
931            &message.instructions[0],
932            &AccountKeys::new(&message.account_keys[0..1], None)
933        )
934        .is_err());
935        let keys = message.account_keys.clone();
936        message.instructions[0].accounts.pop();
937        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
938    }
939
940    #[test]
941    fn test_parse_tower_sync_switch_ix() {
942        let tower_sync = TowerSync::from(vec![(0, 3), (1, 2), (2, 1)]);
943
944        let vote_pubkey = Pubkey::new_unique();
945        let authorized_voter_pubkey = Pubkey::new_unique();
946        let proof_hash = Hash::new_from_array([2; 32]);
947        let instruction = vote_instruction::tower_sync_switch(
948            &vote_pubkey,
949            &authorized_voter_pubkey,
950            tower_sync.clone(),
951            proof_hash,
952        );
953        let mut message = Message::new(&[instruction], None);
954        assert_eq!(
955            parse_vote(
956                &message.instructions[0],
957                &AccountKeys::new(&message.account_keys, None)
958            )
959            .unwrap(),
960            ParsedInstructionEnum {
961                instruction_type: "towersyncswitch".to_string(),
962                info: json!({
963                    "voteAccount": vote_pubkey.to_string(),
964                    "voteAuthority": authorized_voter_pubkey.to_string(),
965                    "towerSync": {
966                        "lockouts": tower_sync.lockouts,
967                        "root": None::<u64>,
968                        "hash": Hash::default().to_string(),
969                        "timestamp": None::<u64>,
970                        "blockId": Hash::default().to_string(),
971                    },
972                    "hash": proof_hash.to_string(),
973                }),
974            }
975        );
976        assert!(parse_vote(
977            &message.instructions[0],
978            &AccountKeys::new(&message.account_keys[0..1], None)
979        )
980        .is_err());
981        let keys = message.account_keys.clone();
982        message.instructions[0].accounts.pop();
983        assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
984    }
985}