Skip to main content

solana_vote/
vote_state_view.rs

1use {
2    self::{
3        field_frames::{
4            AuthorizedVotersListFrame, BlsPubkeyCompressedFrame, BlsPubkeyCompressedView,
5            EpochCreditsItem, EpochCreditsListFrame, PendingDelegatorRewardsView, RootSlotFrame,
6            RootSlotView, VotesFrame,
7        },
8        frame_v1_14_11::VoteStateFrameV1_14_11,
9        frame_v3::VoteStateFrameV3,
10        list_view::ListView,
11    },
12    core::fmt::Debug,
13    field_frames::{CommissionFrame, CommissionView},
14    frame_v4::VoteStateFrameV4,
15    solana_clock::{Epoch, Slot},
16    solana_pubkey::Pubkey,
17    solana_vote_interface::state::{BlockTimestamp, Lockout, BLS_PUBLIC_KEY_COMPRESSED_SIZE},
18    std::sync::Arc,
19};
20#[cfg(feature = "dev-context-only-utils")]
21use {
22    bincode,
23    solana_vote_interface::state::{VoteStateV3, VoteStateV4, VoteStateVersions},
24};
25
26mod field_frames;
27mod frame_v1_14_11;
28mod frame_v3;
29mod frame_v4;
30mod list_view;
31
32#[derive(Debug, PartialEq, Eq)]
33pub enum VoteStateViewError {
34    AccountDataTooSmall,
35    InvalidVotesLength,
36    InvalidRootSlotOption,
37    InvalidBlsPubkeyCompressedOption,
38    InvalidAuthorizedVotersLength,
39    InvalidEpochCreditsLength,
40    OldVersion,
41    UnsupportedVersion,
42}
43
44pub type Result<T> = core::result::Result<T, VoteStateViewError>;
45
46enum Field {
47    NodePubkey,
48    Commission,
49    Votes,
50    RootSlot,
51    AuthorizedVoters,
52    EpochCredits,
53    LastTimestamp,
54}
55
56enum Simd185Field {
57    InflationRewardsCollector,
58    BlockRevenueCollector,
59    BlockRevenueCommission,
60    PendingDelegatorRewards,
61    BlsPubkeyCompressed,
62}
63
64/// A view into a serialized VoteState.
65///
66/// This struct provides access to the VoteState data without
67/// deserializing it. This is done by parsing and caching metadata
68/// about the layout of the serialized VoteState.
69#[derive(Debug, Clone)]
70#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
71pub struct VoteStateView {
72    data: Arc<Vec<u8>>,
73    frame: VoteStateFrame,
74}
75
76impl VoteStateView {
77    pub fn try_new(data: Arc<Vec<u8>>) -> Result<Self> {
78        let frame = VoteStateFrame::try_new(data.as_ref())?;
79        Ok(Self { data, frame })
80    }
81
82    pub fn node_pubkey(&self) -> &Pubkey {
83        let offset = self.frame.offset(Field::NodePubkey);
84        // SAFETY: `frame` was created from `data`.
85        unsafe { &*(self.data.as_ptr().add(offset) as *const Pubkey) }
86    }
87
88    pub fn commission(&self) -> u8 {
89        self.inflation_rewards_commission_view()
90            .commission_percent()
91    }
92
93    pub fn block_revenue_collector(&self) -> Option<&Pubkey> {
94        let offset = self
95            .frame
96            .simd185_field_offset(Simd185Field::BlockRevenueCollector)?;
97        // SAFETY: `frame` was created from `data`.
98        unsafe { Some(&*(self.data.as_ptr().add(offset) as *const Pubkey)) }
99    }
100
101    pub fn inflation_rewards_collector(&self) -> Option<&Pubkey> {
102        let offset = self
103            .frame
104            .simd185_field_offset(Simd185Field::InflationRewardsCollector)?;
105        // SAFETY: `frame` was created from `data`.
106        unsafe { Some(&*(self.data.as_ptr().add(offset) as *const Pubkey)) }
107    }
108
109    pub fn inflation_rewards_commission(&self) -> u16 {
110        self.inflation_rewards_commission_view().commission_bps()
111    }
112
113    pub fn block_revenue_commission(&self) -> u16 {
114        self.block_revenue_commission_view()
115            .map(|view| view.commission_bps())
116            .unwrap_or(10_000)
117    }
118
119    pub fn pending_delegator_rewards(&self) -> u64 {
120        self.pending_delegator_rewards_view()
121            .map(|view| view.value())
122            .unwrap_or(0)
123    }
124
125    pub fn bls_pubkey_compressed(&self) -> Option<[u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE]> {
126        self.bls_pubkey_compressed_view()
127            .and_then(|view| view.pubkey())
128    }
129
130    pub fn votes_iter(&self) -> impl Iterator<Item = Lockout> + '_ {
131        self.votes_view().into_iter().map(|vote| {
132            Lockout::new_with_confirmation_count(vote.slot(), vote.confirmation_count())
133        })
134    }
135
136    #[inline]
137    pub fn votes_len(&self) -> usize {
138        self.votes_view().len()
139    }
140
141    pub fn last_lockout(&self) -> Option<Lockout> {
142        self.votes_view().last().map(|item| {
143            Lockout::new_with_confirmation_count(item.slot(), item.confirmation_count())
144        })
145    }
146
147    pub fn last_voted_slot(&self) -> Option<Slot> {
148        self.votes_view().last().map(|item| item.slot())
149    }
150
151    pub fn root_slot(&self) -> Option<Slot> {
152        self.root_slot_view().root_slot()
153    }
154
155    pub fn get_authorized_voter(&self, epoch: Epoch) -> Option<&Pubkey> {
156        self.authorized_voters_view().get_authorized_voter(epoch)
157    }
158
159    pub fn num_epoch_credits(&self) -> usize {
160        self.epoch_credits_view().len()
161    }
162
163    pub fn epoch_credits_iter(&self) -> impl Iterator<Item = &EpochCreditsItem> + '_ {
164        self.epoch_credits_view().into_iter()
165    }
166
167    pub fn credits(&self) -> u64 {
168        self.epoch_credits_view()
169            .last()
170            .map(|item| item.credits())
171            .unwrap_or(0)
172    }
173
174    pub fn last_timestamp(&self) -> BlockTimestamp {
175        let offset = self.frame.offset(Field::LastTimestamp);
176        // SAFETY: `frame` was created from `data`.
177        let buffer = &self.data[offset..];
178        let mut cursor = std::io::Cursor::new(buffer);
179        BlockTimestamp {
180            slot: solana_serialize_utils::cursor::read_u64(&mut cursor).unwrap(),
181            timestamp: solana_serialize_utils::cursor::read_i64(&mut cursor).unwrap(),
182        }
183    }
184
185    fn inflation_rewards_commission_view(&self) -> CommissionView<'_> {
186        let offset = self.frame.offset(Field::Commission);
187        // SAFETY: `frame` was created from `data`.
188        CommissionView::new(self.frame.commission_frame(), &self.data[offset..])
189    }
190
191    fn block_revenue_commission_view(&self) -> Option<CommissionView<'_>> {
192        let offset = self
193            .frame
194            .simd185_field_offset(Simd185Field::BlockRevenueCommission)?;
195        // SAFETY: `frame` was created from `data`.
196        Some(CommissionView::new(
197            CommissionFrame::new_bps(),
198            &self.data[offset..],
199        ))
200    }
201
202    fn pending_delegator_rewards_view(&self) -> Option<PendingDelegatorRewardsView<'_>> {
203        let offset = self
204            .frame
205            .simd185_field_offset(Simd185Field::PendingDelegatorRewards)?;
206        // SAFETY: `frame` was created from `data`.
207        Some(PendingDelegatorRewardsView::new(&self.data[offset..]))
208    }
209
210    fn bls_pubkey_compressed_view(&self) -> Option<BlsPubkeyCompressedView<'_>> {
211        let offset = self
212            .frame
213            .simd185_field_offset(Simd185Field::BlsPubkeyCompressed)?;
214        let frame = self.frame.bls_pubkey_compressed_frame()?;
215        // SAFETY: `frame` was created from `data`.
216        Some(BlsPubkeyCompressedView::new(frame, &self.data[offset..]))
217    }
218
219    fn votes_view(&self) -> ListView<'_, VotesFrame> {
220        let offset = self.frame.offset(Field::Votes);
221        // SAFETY: `frame` was created from `data`.
222        ListView::new(self.frame.votes_frame(), &self.data[offset..])
223    }
224
225    fn root_slot_view(&self) -> RootSlotView<'_> {
226        let offset = self.frame.offset(Field::RootSlot);
227        // SAFETY: `frame` was created from `data`.
228        RootSlotView::new(self.frame.root_slot_frame(), &self.data[offset..])
229    }
230
231    fn authorized_voters_view(&self) -> ListView<'_, AuthorizedVotersListFrame> {
232        let offset = self.frame.offset(Field::AuthorizedVoters);
233        // SAFETY: `frame` was created from `data`.
234        ListView::new(self.frame.authorized_voters_frame(), &self.data[offset..])
235    }
236
237    fn epoch_credits_view(&self) -> ListView<'_, EpochCreditsListFrame> {
238        let offset = self.frame.offset(Field::EpochCredits);
239        // SAFETY: `frame` was created from `data`.
240        ListView::new(self.frame.epoch_credits_frame(), &self.data[offset..])
241    }
242}
243
244#[cfg(feature = "dev-context-only-utils")]
245impl From<VoteStateV3> for VoteStateView {
246    fn from(vote_state: VoteStateV3) -> Self {
247        let vote_account_data = bincode::serialize(&VoteStateVersions::new_v3(vote_state)).unwrap();
248        VoteStateView::try_new(Arc::new(vote_account_data)).unwrap()
249    }
250}
251
252#[cfg(feature = "dev-context-only-utils")]
253impl From<VoteStateV4> for VoteStateView {
254    fn from(vote_state: VoteStateV4) -> Self {
255        let vote_account_data = bincode::serialize(&VoteStateVersions::new_v4(vote_state)).unwrap();
256        VoteStateView::try_new(Arc::new(vote_account_data)).unwrap()
257    }
258}
259
260#[derive(Debug, Clone)]
261#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
262enum VoteStateFrame {
263    V1_14_11(VoteStateFrameV1_14_11),
264    V3(VoteStateFrameV3),
265    V4(VoteStateFrameV4),
266}
267
268impl VoteStateFrame {
269    /// Parse a serialized vote state and verify structure.
270    fn try_new(bytes: &[u8]) -> Result<Self> {
271        let version = {
272            let mut cursor = std::io::Cursor::new(bytes);
273            solana_serialize_utils::cursor::read_u32(&mut cursor)
274                .map_err(|_err| VoteStateViewError::AccountDataTooSmall)?
275        };
276
277        Ok(match version {
278            0 => return Err(VoteStateViewError::OldVersion),
279            1 => Self::V1_14_11(VoteStateFrameV1_14_11::try_new(bytes)?),
280            2 => Self::V3(VoteStateFrameV3::try_new(bytes)?),
281            3 => Self::V4(VoteStateFrameV4::try_new(bytes)?),
282            _ => return Err(VoteStateViewError::UnsupportedVersion),
283        })
284    }
285
286    fn offset(&self, field: Field) -> usize {
287        match &self {
288            Self::V1_14_11(frame) => frame.field_offset(field),
289            Self::V3(frame) => frame.field_offset(field),
290            Self::V4(frame) => frame.field_offset(field),
291        }
292    }
293
294    fn simd185_field_offset(&self, field: Simd185Field) -> Option<usize> {
295        match &self {
296            Self::V1_14_11(_frame) => None,
297            Self::V3(_frame) => None,
298            Self::V4(frame) => Some(frame.simd185_field_offset(field)),
299        }
300    }
301
302    fn commission_frame(&self) -> CommissionFrame {
303        match &self {
304            Self::V1_14_11(_) => CommissionFrame::new_percent(),
305            Self::V3(_) => CommissionFrame::new_percent(),
306            Self::V4(_) => CommissionFrame::new_bps(),
307        }
308    }
309
310    fn bls_pubkey_compressed_frame(&self) -> Option<BlsPubkeyCompressedFrame> {
311        match &self {
312            Self::V1_14_11 { .. } | Self::V3 { .. } => None,
313            Self::V4(frame) => Some(frame.bls_pubkey_compressed_frame),
314        }
315    }
316
317    fn votes_frame(&self) -> VotesFrame {
318        match &self {
319            Self::V1_14_11(frame) => VotesFrame::Lockout(frame.votes_frame),
320            Self::V3(frame) => VotesFrame::Landed(frame.votes_frame),
321            Self::V4(frame) => VotesFrame::Landed(frame.votes_frame),
322        }
323    }
324
325    fn root_slot_frame(&self) -> RootSlotFrame {
326        match &self {
327            Self::V1_14_11(vote_frame) => vote_frame.root_slot_frame,
328            Self::V3(vote_frame) => vote_frame.root_slot_frame,
329            Self::V4(vote_frame) => vote_frame.root_slot_frame,
330        }
331    }
332
333    fn authorized_voters_frame(&self) -> AuthorizedVotersListFrame {
334        match &self {
335            Self::V1_14_11(frame) => frame.authorized_voters_frame,
336            Self::V3(frame) => frame.authorized_voters_frame,
337            Self::V4(frame) => frame.authorized_voters_frame,
338        }
339    }
340
341    fn epoch_credits_frame(&self) -> EpochCreditsListFrame {
342        match &self {
343            Self::V1_14_11(frame) => frame.epoch_credits_frame,
344            Self::V3(frame) => frame.epoch_credits_frame,
345            Self::V4(frame) => frame.epoch_credits_frame,
346        }
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use {
353        super::*,
354        arbitrary::{Arbitrary, Unstructured},
355        serde::{Deserialize, Serialize},
356        solana_clock::Clock,
357        solana_vote_interface::{
358            authorized_voters::AuthorizedVoters,
359            state::{
360                LandedVote, VoteInit, VoteState1_14_11, VoteStateV3, VoteStateV4,
361                VoteStateVersions, MAX_EPOCH_CREDITS_HISTORY, MAX_LOCKOUT_HISTORY,
362            },
363        },
364        std::collections::VecDeque,
365    };
366
367    #[derive(Debug, Clone, Deserialize, Serialize)]
368    enum TestVoteStateVersions {
369        V0_23_5,
370        V1_14_11,
371        V3,
372        V4(Box<VoteStateV4>),
373    }
374
375    fn new_test_vote_state_v4() -> VoteStateV4 {
376        let votes = (0..MAX_LOCKOUT_HISTORY)
377            .map(|i| LandedVote {
378                latency: i as u8,
379                lockout: Lockout::new_with_confirmation_count(i as u64, i as u32),
380            })
381            .collect();
382
383        VoteStateV4 {
384            node_pubkey: Pubkey::new_unique(),
385            authorized_withdrawer: Pubkey::new_unique(),
386            inflation_rewards_collector: Pubkey::new_unique(),
387            block_revenue_collector: Pubkey::new_unique(),
388            inflation_rewards_commission_bps: 42,
389            block_revenue_commission_bps: 42,
390            pending_delegator_rewards: 42,
391            bls_pubkey_compressed: Some([42; BLS_PUBLIC_KEY_COMPRESSED_SIZE]),
392            votes,
393            root_slot: Some(42),
394            authorized_voters: AuthorizedVoters::new(42, Pubkey::new_unique()),
395            epoch_credits: vec![(42, 42, 42)],
396            last_timestamp: BlockTimestamp {
397                slot: 42,
398                timestamp: 42,
399            },
400        }
401    }
402
403    fn new_test_vote_state_v3() -> VoteStateV3 {
404        let mut target_vote_state = VoteStateV3::new(
405            &VoteInit {
406                node_pubkey: Pubkey::new_unique(),
407                authorized_voter: Pubkey::new_unique(),
408                authorized_withdrawer: Pubkey::new_unique(),
409                commission: 42,
410            },
411            &Clock::default(),
412        );
413
414        target_vote_state
415            .set_new_authorized_voter(
416                &Pubkey::new_unique(), // authorized_pubkey
417                0,                     // current_epoch
418                1,                     // target_epoch
419                |_| Ok(()),
420            )
421            .unwrap();
422
423        target_vote_state.root_slot = Some(42);
424        target_vote_state.epoch_credits.push((42, 42, 42));
425        target_vote_state.last_timestamp = BlockTimestamp {
426            slot: 42,
427            timestamp: 42,
428        };
429        for i in 0..MAX_LOCKOUT_HISTORY {
430            target_vote_state.votes.push_back(LandedVote {
431                latency: i as u8,
432                lockout: Lockout::new_with_confirmation_count(i as u64, i as u32),
433            });
434        }
435
436        target_vote_state
437    }
438
439    #[test]
440    fn test_vote_state_view_v4() {
441        let target_vote_state = new_test_vote_state_v4();
442        let target_vote_state_versions =
443            TestVoteStateVersions::V4(Box::new(target_vote_state.clone()));
444        let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
445        let vote_state_view = VoteStateView::try_new(Arc::new(vote_state_buf)).unwrap();
446        assert_eq_vote_state_v4(&vote_state_view, &target_vote_state);
447    }
448
449    #[test]
450    fn test_vote_state_view_v4_default() {
451        let target_vote_state = VoteStateV4::default();
452        let target_vote_state_versions =
453            TestVoteStateVersions::V4(Box::new(target_vote_state.clone()));
454        let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
455        let vote_state_view = VoteStateView::try_new(Arc::new(vote_state_buf)).unwrap();
456        assert_eq_vote_state_v4(&vote_state_view, &target_vote_state);
457    }
458
459    #[test]
460    fn test_vote_state_view_v4_arbitrary() {
461        // variant
462        // provide 4x the minimum struct size in bytes to ensure we typically touch every field
463        let struct_bytes_x4 = VoteStateV3::size_of() * 4;
464        for _ in 0..100 {
465            let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
466            let mut unstructured = Unstructured::new(&raw_data);
467
468            let mut target_vote_state = VoteStateV4::arbitrary(&mut unstructured).unwrap();
469            target_vote_state.votes.truncate(MAX_LOCKOUT_HISTORY);
470            target_vote_state
471                .epoch_credits
472                .truncate(MAX_EPOCH_CREDITS_HISTORY);
473            if target_vote_state.authorized_voters.len() >= u8::MAX as usize {
474                continue;
475            }
476
477            let target_vote_state_versions =
478                TestVoteStateVersions::V4(Box::new(target_vote_state.clone()));
479            let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
480            let vote_state_view = VoteStateView::try_new(Arc::new(vote_state_buf)).unwrap();
481            assert_eq_vote_state_v4(&vote_state_view, &target_vote_state);
482        }
483    }
484    #[test]
485    fn test_vote_state_view_v3() {
486        let target_vote_state = new_test_vote_state_v3();
487        let target_vote_state_versions = VoteStateVersions::V3(Box::new(target_vote_state.clone()));
488        let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
489        let vote_state_view = VoteStateView::try_new(Arc::new(vote_state_buf)).unwrap();
490        assert_eq_vote_state_v3(&vote_state_view, &target_vote_state);
491    }
492
493    #[test]
494    fn test_vote_state_view_v3_default() {
495        let target_vote_state = VoteStateV3::default();
496        let target_vote_state_versions = VoteStateVersions::V3(Box::new(target_vote_state.clone()));
497        let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
498        let vote_state_view = VoteStateView::try_new(Arc::new(vote_state_buf)).unwrap();
499        assert_eq_vote_state_v3(&vote_state_view, &target_vote_state);
500    }
501
502    #[test]
503    fn test_vote_state_view_v3_arbitrary() {
504        // variant
505        // provide 4x the minimum struct size in bytes to ensure we typically touch every field
506        let struct_bytes_x4 = VoteStateV3::size_of() * 4;
507        for _ in 0..100 {
508            let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
509            let mut unstructured = Unstructured::new(&raw_data);
510
511            let mut target_vote_state = VoteStateV3::arbitrary(&mut unstructured).unwrap();
512            target_vote_state.votes.truncate(MAX_LOCKOUT_HISTORY);
513            target_vote_state
514                .epoch_credits
515                .truncate(MAX_EPOCH_CREDITS_HISTORY);
516            if target_vote_state.authorized_voters().len() >= u8::MAX as usize {
517                continue;
518            }
519
520            let target_vote_state_versions =
521                VoteStateVersions::V3(Box::new(target_vote_state.clone()));
522            let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
523            let vote_state_view = VoteStateView::try_new(Arc::new(vote_state_buf)).unwrap();
524            assert_eq_vote_state_v3(&vote_state_view, &target_vote_state);
525        }
526    }
527
528    #[test]
529    fn test_vote_state_view_1_14_11() {
530        let target_vote_state: VoteState1_14_11 = new_test_vote_state_v3().into();
531        let target_vote_state_versions =
532            VoteStateVersions::V1_14_11(Box::new(target_vote_state.clone()));
533        let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
534        let vote_state_view = VoteStateView::try_new(Arc::new(vote_state_buf)).unwrap();
535        assert_eq_vote_state_1_14_11(&vote_state_view, &target_vote_state);
536    }
537
538    #[test]
539    fn test_vote_state_view_1_14_11_default() {
540        let target_vote_state = VoteState1_14_11::default();
541        let target_vote_state_versions =
542            VoteStateVersions::V1_14_11(Box::new(target_vote_state.clone()));
543        let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
544        let vote_state_view = VoteStateView::try_new(Arc::new(vote_state_buf)).unwrap();
545        assert_eq_vote_state_1_14_11(&vote_state_view, &target_vote_state);
546    }
547
548    #[test]
549    fn test_vote_state_view_1_14_11_arbitrary() {
550        // variant
551        // provide 4x the minimum struct size in bytes to ensure we typically touch every field
552        let struct_bytes_x4 = std::mem::size_of::<VoteState1_14_11>() * 4;
553        for _ in 0..100 {
554            let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
555            let mut unstructured = Unstructured::new(&raw_data);
556
557            let mut target_vote_state = VoteState1_14_11::arbitrary(&mut unstructured).unwrap();
558            target_vote_state.votes.truncate(MAX_LOCKOUT_HISTORY);
559            target_vote_state
560                .epoch_credits
561                .truncate(MAX_EPOCH_CREDITS_HISTORY);
562            if target_vote_state.authorized_voters.len() >= u8::MAX as usize {
563                let (&first, &voter) = target_vote_state.authorized_voters.first().unwrap();
564                let mut authorized_voters = AuthorizedVoters::new(first, voter);
565                for (epoch, pubkey) in target_vote_state.authorized_voters.iter().skip(1).take(10) {
566                    authorized_voters.insert(*epoch, *pubkey);
567                }
568                target_vote_state.authorized_voters = authorized_voters;
569            }
570
571            let target_vote_state_versions =
572                VoteStateVersions::V1_14_11(Box::new(target_vote_state.clone()));
573            let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
574            let vote_state_view = VoteStateView::try_new(Arc::new(vote_state_buf)).unwrap();
575            assert_eq_vote_state_1_14_11(&vote_state_view, &target_vote_state);
576        }
577    }
578
579    fn assert_eq_vote_state_v4(vote_state_view: &VoteStateView, vote_state: &VoteStateV4) {
580        assert_eq!(vote_state_view.node_pubkey(), &vote_state.node_pubkey);
581        assert_eq!(
582            vote_state_view.inflation_rewards_collector(),
583            Some(&vote_state.inflation_rewards_collector)
584        );
585        assert_eq!(
586            vote_state_view.block_revenue_collector(),
587            Some(&vote_state.block_revenue_collector)
588        );
589        assert_eq!(
590            vote_state_view.inflation_rewards_commission(),
591            vote_state.inflation_rewards_commission_bps
592        );
593        assert_eq!(
594            vote_state_view.block_revenue_commission(),
595            vote_state.block_revenue_commission_bps
596        );
597        assert_eq!(
598            vote_state_view.pending_delegator_rewards(),
599            vote_state.pending_delegator_rewards
600        );
601        assert_eq!(
602            vote_state_view.bls_pubkey_compressed(),
603            vote_state.bls_pubkey_compressed
604        );
605        let view_votes = vote_state_view.votes_iter().collect::<Vec<_>>();
606        let state_votes = vote_state
607            .votes
608            .iter()
609            .map(|vote| vote.lockout)
610            .collect::<Vec<_>>();
611        assert_eq!(view_votes, state_votes);
612        assert_eq!(vote_state_view.root_slot(), vote_state.root_slot);
613
614        if let Some((first_voter_epoch, first_voter)) = vote_state.authorized_voters.first() {
615            assert_eq!(
616                vote_state_view.get_authorized_voter(*first_voter_epoch),
617                Some(first_voter)
618            );
619
620            let (last_voter_epoch, last_voter) = vote_state.authorized_voters.last().unwrap();
621            assert_eq!(
622                vote_state_view.get_authorized_voter(*last_voter_epoch),
623                Some(last_voter)
624            );
625            assert_eq!(
626                vote_state_view.get_authorized_voter(u64::MAX),
627                Some(last_voter)
628            );
629        } else {
630            assert_eq!(vote_state_view.get_authorized_voter(u64::MAX), None);
631        }
632
633        assert_eq!(
634            vote_state_view.num_epoch_credits(),
635            vote_state.epoch_credits.len()
636        );
637        let view_credits: Vec<(Epoch, u64, u64)> = vote_state_view
638            .epoch_credits_iter()
639            .map(Into::into)
640            .collect::<Vec<_>>();
641        assert_eq!(view_credits, vote_state.epoch_credits);
642
643        assert_eq!(
644            vote_state_view.credits(),
645            vote_state.epoch_credits.last().map(|x| x.1).unwrap_or(0)
646        );
647        assert_eq!(vote_state_view.last_timestamp(), vote_state.last_timestamp);
648    }
649
650    fn assert_eq_vote_state_v3(vote_state_view: &VoteStateView, vote_state: &VoteStateV3) {
651        assert_eq!(vote_state_view.node_pubkey(), &vote_state.node_pubkey);
652        assert_eq!(vote_state_view.commission(), vote_state.commission);
653        let view_votes = vote_state_view.votes_iter().collect::<Vec<_>>();
654        let state_votes = vote_state
655            .votes
656            .iter()
657            .map(|vote| vote.lockout)
658            .collect::<Vec<_>>();
659        assert_eq!(view_votes, state_votes);
660        assert_eq!(
661            vote_state_view.last_lockout(),
662            vote_state.last_lockout().copied()
663        );
664        assert_eq!(
665            vote_state_view.last_voted_slot(),
666            vote_state.last_voted_slot(),
667        );
668        assert_eq!(vote_state_view.root_slot(), vote_state.root_slot);
669
670        if let Some((first_voter_epoch, first_voter)) = vote_state.authorized_voters().first() {
671            assert_eq!(
672                vote_state_view.get_authorized_voter(*first_voter_epoch),
673                Some(first_voter)
674            );
675
676            let (last_voter_epoch, last_voter) = vote_state.authorized_voters().last().unwrap();
677            assert_eq!(
678                vote_state_view.get_authorized_voter(*last_voter_epoch),
679                Some(last_voter)
680            );
681            assert_eq!(
682                vote_state_view.get_authorized_voter(u64::MAX),
683                Some(last_voter)
684            );
685        } else {
686            assert_eq!(vote_state_view.get_authorized_voter(u64::MAX), None);
687        }
688
689        assert_eq!(
690            vote_state_view.num_epoch_credits(),
691            vote_state.epoch_credits.len()
692        );
693        let view_credits: Vec<(Epoch, u64, u64)> = vote_state_view
694            .epoch_credits_iter()
695            .map(Into::into)
696            .collect::<Vec<_>>();
697        assert_eq!(view_credits, vote_state.epoch_credits);
698
699        assert_eq!(
700            vote_state_view.credits(),
701            vote_state.epoch_credits.last().map(|x| x.1).unwrap_or(0)
702        );
703        assert_eq!(vote_state_view.last_timestamp(), vote_state.last_timestamp);
704    }
705
706    fn assert_eq_vote_state_1_14_11(
707        vote_state_view: &VoteStateView,
708        vote_state: &VoteState1_14_11,
709    ) {
710        assert_eq!(vote_state_view.node_pubkey(), &vote_state.node_pubkey);
711        assert_eq!(vote_state_view.commission(), vote_state.commission);
712        let view_votes = vote_state_view.votes_iter().collect::<VecDeque<_>>();
713        assert_eq!(view_votes, vote_state.votes);
714        assert_eq!(
715            vote_state_view.last_lockout(),
716            vote_state.votes.back().copied()
717        );
718        assert_eq!(
719            vote_state_view.last_voted_slot(),
720            vote_state.votes.back().map(|lockout| lockout.slot()),
721        );
722        assert_eq!(vote_state_view.root_slot(), vote_state.root_slot);
723
724        if let Some((first_voter_epoch, first_voter)) = vote_state.authorized_voters.first() {
725            assert_eq!(
726                vote_state_view.get_authorized_voter(*first_voter_epoch),
727                Some(first_voter)
728            );
729
730            let (last_voter_epoch, last_voter) = vote_state.authorized_voters.last().unwrap();
731            assert_eq!(
732                vote_state_view.get_authorized_voter(*last_voter_epoch),
733                Some(last_voter)
734            );
735            assert_eq!(
736                vote_state_view.get_authorized_voter(u64::MAX),
737                Some(last_voter)
738            );
739        } else {
740            assert_eq!(vote_state_view.get_authorized_voter(u64::MAX), None);
741        }
742
743        assert_eq!(
744            vote_state_view.num_epoch_credits(),
745            vote_state.epoch_credits.len()
746        );
747        let view_credits: Vec<(Epoch, u64, u64)> = vote_state_view
748            .epoch_credits_iter()
749            .map(Into::into)
750            .collect::<Vec<_>>();
751        assert_eq!(view_credits, vote_state.epoch_credits);
752
753        assert_eq!(
754            vote_state_view.credits(),
755            vote_state.epoch_credits.last().map(|x| x.1).unwrap_or(0)
756        );
757        assert_eq!(vote_state_view.last_timestamp(), vote_state.last_timestamp);
758    }
759
760    #[test]
761    fn test_vote_state_view_too_small() {
762        for i in 0..4 {
763            let vote_data = Arc::new(vec![0; i]);
764            let vote_state_view_err = VoteStateView::try_new(vote_data).unwrap_err();
765            assert_eq!(vote_state_view_err, VoteStateViewError::AccountDataTooSmall);
766        }
767    }
768
769    #[test]
770    fn test_vote_state_view_old_version() {
771        let vote_data = Arc::new(0u32.to_le_bytes().to_vec());
772        let vote_state_view_err = VoteStateView::try_new(vote_data).unwrap_err();
773        assert_eq!(vote_state_view_err, VoteStateViewError::OldVersion);
774    }
775
776    #[test]
777    fn test_vote_state_view_unsupported_version() {
778        let vote_data = Arc::new(4u32.to_le_bytes().to_vec());
779        let vote_state_view_err = VoteStateView::try_new(vote_data).unwrap_err();
780        assert_eq!(vote_state_view_err, VoteStateViewError::UnsupportedVersion);
781    }
782}