atlas_vote_interface/state/
vote_state_versions.rs

1#[cfg(test)]
2use arbitrary::{Arbitrary, Unstructured};
3use {
4    crate::{
5        authorized_voters::AuthorizedVoters,
6        state::{
7            vote_state_0_23_5::VoteState0_23_5, vote_state_1_14_11::VoteState1_14_11, CircBuf,
8            LandedVote, Lockout, VoteStateV3, VoteStateV4,
9        },
10    },
11    atlas_instruction::error::InstructionError,
12    atlas_pubkey::Pubkey,
13    std::collections::VecDeque,
14};
15
16#[cfg_attr(
17    feature = "serde",
18    derive(serde_derive::Deserialize, serde_derive::Serialize)
19)]
20#[derive(Debug, PartialEq, Eq, Clone)]
21pub enum VoteStateVersions {
22    V0_23_5(Box<VoteState0_23_5>),
23    V1_14_11(Box<VoteState1_14_11>),
24    V3(Box<VoteStateV3>),
25    V4(Box<VoteStateV4>),
26}
27
28impl VoteStateVersions {
29    pub fn new_v3(vote_state: VoteStateV3) -> Self {
30        Self::V3(Box::new(vote_state))
31    }
32
33    pub fn new_v4(vote_state: VoteStateV4) -> Self {
34        Self::V4(Box::new(vote_state))
35    }
36
37    /// Convert from vote state `V0_23_5`, `V1_14_11`, or `V3` to `V3`.
38    ///
39    /// NOTE: Does not support conversion from `V4`. Attempting to convert from
40    /// v4 to v3 will throw an error.
41    pub fn try_convert_to_v3(self) -> Result<VoteStateV3, InstructionError> {
42        match self {
43            VoteStateVersions::V0_23_5(state) => {
44                let authorized_voters = if state.is_uninitialized() {
45                    AuthorizedVoters::default()
46                } else {
47                    AuthorizedVoters::new(state.authorized_voter_epoch, state.authorized_voter)
48                };
49
50                Ok(VoteStateV3 {
51                    node_pubkey: state.node_pubkey,
52
53                    authorized_withdrawer: state.authorized_withdrawer,
54
55                    commission: state.commission,
56
57                    votes: Self::landed_votes_from_lockouts(state.votes),
58
59                    root_slot: state.root_slot,
60
61                    authorized_voters,
62
63                    prior_voters: CircBuf::default(),
64
65                    epoch_credits: state.epoch_credits.clone(),
66
67                    last_timestamp: state.last_timestamp.clone(),
68                })
69            }
70
71            VoteStateVersions::V1_14_11(state) => Ok(VoteStateV3 {
72                node_pubkey: state.node_pubkey,
73                authorized_withdrawer: state.authorized_withdrawer,
74                commission: state.commission,
75
76                votes: Self::landed_votes_from_lockouts(state.votes),
77
78                root_slot: state.root_slot,
79
80                authorized_voters: state.authorized_voters.clone(),
81
82                prior_voters: state.prior_voters,
83
84                epoch_credits: state.epoch_credits,
85
86                last_timestamp: state.last_timestamp,
87            }),
88
89            VoteStateVersions::V3(state) => Ok(*state),
90
91            // Cannot convert V4 to V3.
92            VoteStateVersions::V4(_) => Err(InstructionError::InvalidArgument),
93        }
94    }
95
96    // Currently, all versions can be converted to v4 without data loss, so
97    // this function returns `Ok(..)`. However, future versions may not be
98    // convertible to v4 without data loss, so this function returns a `Result`
99    // for forward compatibility.
100    pub fn try_convert_to_v4(self, vote_pubkey: &Pubkey) -> Result<VoteStateV4, InstructionError> {
101        Ok(match self {
102            VoteStateVersions::V0_23_5(state) => {
103                let authorized_voters = if state.is_uninitialized() {
104                    AuthorizedVoters::default()
105                } else {
106                    AuthorizedVoters::new(state.authorized_voter_epoch, state.authorized_voter)
107                };
108
109                VoteStateV4 {
110                    node_pubkey: state.node_pubkey,
111                    authorized_withdrawer: state.authorized_withdrawer,
112                    inflation_rewards_collector: *vote_pubkey,
113                    block_revenue_collector: state.node_pubkey,
114                    inflation_rewards_commission_bps: u16::from(state.commission)
115                        .saturating_mul(100),
116                    block_revenue_commission_bps: 10_000u16,
117                    pending_delegator_rewards: 0,
118                    bls_pubkey_compressed: None,
119                    votes: Self::landed_votes_from_lockouts(state.votes),
120                    root_slot: state.root_slot,
121                    authorized_voters,
122                    epoch_credits: state.epoch_credits.clone(),
123                    last_timestamp: state.last_timestamp.clone(),
124                }
125            }
126
127            VoteStateVersions::V1_14_11(state) => VoteStateV4 {
128                node_pubkey: state.node_pubkey,
129                authorized_withdrawer: state.authorized_withdrawer,
130                inflation_rewards_collector: *vote_pubkey,
131                block_revenue_collector: state.node_pubkey,
132                inflation_rewards_commission_bps: u16::from(state.commission).saturating_mul(100),
133                block_revenue_commission_bps: 10_000u16,
134                pending_delegator_rewards: 0,
135                bls_pubkey_compressed: None,
136                votes: Self::landed_votes_from_lockouts(state.votes),
137                root_slot: state.root_slot,
138                authorized_voters: state.authorized_voters.clone(),
139                epoch_credits: state.epoch_credits,
140                last_timestamp: state.last_timestamp,
141            },
142
143            VoteStateVersions::V3(state) => VoteStateV4 {
144                node_pubkey: state.node_pubkey,
145                authorized_withdrawer: state.authorized_withdrawer,
146                inflation_rewards_collector: *vote_pubkey,
147                block_revenue_collector: state.node_pubkey,
148                inflation_rewards_commission_bps: u16::from(state.commission).saturating_mul(100),
149                block_revenue_commission_bps: 10_000u16,
150                pending_delegator_rewards: 0,
151                bls_pubkey_compressed: None,
152                votes: state.votes,
153                root_slot: state.root_slot,
154                authorized_voters: state.authorized_voters,
155                epoch_credits: state.epoch_credits,
156                last_timestamp: state.last_timestamp,
157            },
158
159            VoteStateVersions::V4(state) => *state,
160        })
161    }
162
163    fn landed_votes_from_lockouts(lockouts: VecDeque<Lockout>) -> VecDeque<LandedVote> {
164        lockouts.into_iter().map(|lockout| lockout.into()).collect()
165    }
166
167    pub fn is_uninitialized(&self) -> bool {
168        match self {
169            VoteStateVersions::V0_23_5(vote_state) => vote_state.is_uninitialized(),
170
171            VoteStateVersions::V1_14_11(vote_state) => vote_state.is_uninitialized(),
172
173            VoteStateVersions::V3(vote_state) => vote_state.is_uninitialized(),
174
175            // As per SIMD-0185, v4 is always initialized.
176            VoteStateVersions::V4(_) => false,
177        }
178    }
179
180    pub fn is_correct_size_and_initialized(data: &[u8]) -> bool {
181        VoteStateV4::is_correct_size_and_initialized(data)
182            || VoteStateV3::is_correct_size_and_initialized(data)
183            || VoteState1_14_11::is_correct_size_and_initialized(data)
184    }
185}
186
187#[cfg(test)]
188impl Arbitrary<'_> for VoteStateVersions {
189    fn arbitrary(u: &mut Unstructured<'_>) -> arbitrary::Result<Self> {
190        let variant = u.choose_index(3)?;
191        match variant {
192            0 => Ok(Self::V4(Box::new(VoteStateV4::arbitrary(u)?))),
193            1 => Ok(Self::V3(Box::new(VoteStateV3::arbitrary(u)?))),
194            2 => Ok(Self::V1_14_11(Box::new(VoteState1_14_11::arbitrary(u)?))),
195            _ => unreachable!(),
196        }
197    }
198}