solana_account_decoder_wasm/
parse_vote.rs

1use 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/// A wrapper enum for consistency across programs
75#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
76#[serde(rename_all = "camelCase", tag = "type", content = "info")]
77pub enum VoteAccountType {
78	Vote(UiVoteState),
79}
80
81/// A duplicate representation of `VoteState` for pretty JSON serialization
82#[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}