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#[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 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 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 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 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 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 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 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 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 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 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 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 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 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(), 0, 1, |_| 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 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 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 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}