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