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