solana_account_decoder_wasm/
parse_vote.rs1use serde::Deserialize;
2use serde::Serialize;
3use solana_clock::Epoch;
4use solana_clock::Slot;
5use solana_pubkey::Pubkey;
6use solana_vote_interface::state::BlockTimestamp;
7use solana_vote_interface::state::Lockout;
8use solana_vote_interface::state::VoteState;
9
10use crate::StringAmount;
11use crate::parse_account_data::ParseAccountError;
12
13pub fn parse_vote(data: &[u8]) -> Result<VoteAccountType, ParseAccountError> {
14 let mut vote_state = VoteState::deserialize(data).map_err(ParseAccountError::from)?;
15 let epoch_credits = vote_state
16 .epoch_credits()
17 .iter()
18 .map(|(epoch, credits, previous_credits)| {
19 UiEpochCredits {
20 epoch: *epoch,
21 credits: credits.to_string(),
22 previous_credits: previous_credits.to_string(),
23 }
24 })
25 .collect();
26 let votes = vote_state
27 .votes
28 .iter()
29 .map(|lockout| {
30 UiLockout {
31 slot: lockout.slot(),
32 confirmation_count: lockout.confirmation_count(),
33 }
34 })
35 .collect();
36 let authorized_voters = vote_state
37 .authorized_voters()
38 .iter()
39 .map(|(epoch, authorized_voter)| {
40 UiAuthorizedVoters {
41 epoch: *epoch,
42 authorized_voter: authorized_voter.to_string(),
43 }
44 })
45 .collect();
46 let prior_voters = vote_state
47 .prior_voters()
48 .buf()
49 .iter()
50 .filter(|(pubkey, ..)| pubkey != &Pubkey::default())
51 .map(
52 |(authorized_pubkey, epoch_of_last_authorized_switch, target_epoch)| {
53 UiPriorVoters {
54 authorized_pubkey: authorized_pubkey.to_string(),
55 epoch_of_last_authorized_switch: *epoch_of_last_authorized_switch,
56 target_epoch: *target_epoch,
57 }
58 },
59 )
60 .collect();
61 Ok(VoteAccountType::Vote(UiVoteState {
62 node_pubkey: vote_state.node_pubkey.to_string(),
63 authorized_withdrawer: vote_state.authorized_withdrawer.to_string(),
64 commission: vote_state.commission,
65 votes,
66 root_slot: vote_state.root_slot,
67 authorized_voters,
68 prior_voters,
69 epoch_credits,
70 last_timestamp: vote_state.last_timestamp,
71 }))
72}
73
74#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
76#[serde(rename_all = "camelCase", tag = "type", content = "info")]
77pub enum VoteAccountType {
78 Vote(UiVoteState),
79}
80
81#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
83#[serde(rename_all = "camelCase")]
84pub struct UiVoteState {
85 node_pubkey: String,
86 authorized_withdrawer: String,
87 commission: u8,
88 votes: Vec<UiLockout>,
89 root_slot: Option<Slot>,
90 authorized_voters: Vec<UiAuthorizedVoters>,
91 prior_voters: Vec<UiPriorVoters>,
92 epoch_credits: Vec<UiEpochCredits>,
93 last_timestamp: BlockTimestamp,
94}
95
96#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
97#[serde(rename_all = "camelCase")]
98struct UiLockout {
99 slot: Slot,
100 confirmation_count: u32,
101}
102
103impl From<&Lockout> for UiLockout {
104 fn from(lockout: &Lockout) -> Self {
105 Self {
106 slot: lockout.slot(),
107 confirmation_count: lockout.confirmation_count(),
108 }
109 }
110}
111
112#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
113#[serde(rename_all = "camelCase")]
114struct UiAuthorizedVoters {
115 epoch: Epoch,
116 authorized_voter: String,
117}
118
119#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
120#[serde(rename_all = "camelCase")]
121struct UiPriorVoters {
122 authorized_pubkey: String,
123 epoch_of_last_authorized_switch: Epoch,
124 target_epoch: Epoch,
125}
126
127#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
128#[serde(rename_all = "camelCase")]
129struct UiEpochCredits {
130 epoch: Epoch,
131 credits: StringAmount,
132 previous_credits: StringAmount,
133}
134
135#[cfg(test)]
136mod test {
137 use solana_vote_interface::state::VoteStateVersions;
138
139 use super::*;
140
141 #[test]
142 fn test_parse_vote() {
143 let vote_state = VoteState::default();
144 let mut vote_account_data: Vec<u8> = vec![0; VoteState::size_of()];
145 let versioned = VoteStateVersions::new_current(vote_state);
146 VoteState::serialize(&versioned, &mut vote_account_data).unwrap();
147 let expected_vote_state = UiVoteState {
148 node_pubkey: Pubkey::default().to_string(),
149 authorized_withdrawer: Pubkey::default().to_string(),
150 ..UiVoteState::default()
151 };
152 assert_eq!(
153 parse_vote(&vote_account_data).unwrap(),
154 VoteAccountType::Vote(expected_vote_state)
155 );
156
157 let bad_data = vec![0; 4];
158 assert!(parse_vote(&bad_data).is_err());
159 }
160}