solana_vote_interface/state/
mod.rs

1//! Vote state
2
3#[cfg(feature = "dev-context-only-utils")]
4use arbitrary::Arbitrary;
5#[cfg(feature = "serde")]
6use serde_derive::{Deserialize, Serialize};
7#[cfg(feature = "frozen-abi")]
8use solana_frozen_abi_macro::AbiExample;
9use {
10    crate::authorized_voters::AuthorizedVoters,
11    solana_clock::{Epoch, Slot, UnixTimestamp},
12    solana_pubkey::Pubkey,
13    solana_rent::Rent,
14    std::{collections::VecDeque, fmt::Debug},
15};
16#[cfg(test)]
17use {arbitrary::Unstructured, solana_epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET};
18
19mod vote_state_0_23_5;
20pub mod vote_state_1_14_11;
21pub use vote_state_1_14_11::*;
22pub mod vote_state_versions;
23pub use vote_state_versions::*;
24pub mod vote_state_v3;
25pub use vote_state_v3::VoteState;
26mod vote_instruction_data;
27pub use vote_instruction_data::*;
28
29// Maximum number of votes to keep around, tightly coupled with epoch_schedule::MINIMUM_SLOTS_PER_EPOCH
30pub const MAX_LOCKOUT_HISTORY: usize = 31;
31pub const INITIAL_LOCKOUT: usize = 2;
32
33// Maximum number of credits history to keep around
34pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
35
36// Offset of VoteState::prior_voters, for determining initialization status without deserialization
37const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 114;
38
39// Number of slots of grace period for which maximum vote credits are awarded - votes landing within this number of slots of the slot that is being voted on are awarded full credits.
40pub const VOTE_CREDITS_GRACE_SLOTS: u8 = 2;
41
42// Maximum number of credits to award for a vote; this number of credits is awarded to votes on slots that land within the grace period. After that grace period, vote credits are reduced.
43pub const VOTE_CREDITS_MAXIMUM_PER_SLOT: u8 = 16;
44
45#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
46#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
47#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
48#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
49pub struct Lockout {
50    slot: Slot,
51    confirmation_count: u32,
52}
53
54impl Lockout {
55    pub fn new(slot: Slot) -> Self {
56        Self::new_with_confirmation_count(slot, 1)
57    }
58
59    pub fn new_with_confirmation_count(slot: Slot, confirmation_count: u32) -> Self {
60        Self {
61            slot,
62            confirmation_count,
63        }
64    }
65
66    // The number of slots for which this vote is locked
67    pub fn lockout(&self) -> u64 {
68        (INITIAL_LOCKOUT as u64).wrapping_pow(std::cmp::min(
69            self.confirmation_count(),
70            MAX_LOCKOUT_HISTORY as u32,
71        ))
72    }
73
74    // The last slot at which a vote is still locked out. Validators should not
75    // vote on a slot in another fork which is less than or equal to this slot
76    // to avoid having their stake slashed.
77    pub fn last_locked_out_slot(&self) -> Slot {
78        self.slot.saturating_add(self.lockout())
79    }
80
81    pub fn is_locked_out_at_slot(&self, slot: Slot) -> bool {
82        self.last_locked_out_slot() >= slot
83    }
84
85    pub fn slot(&self) -> Slot {
86        self.slot
87    }
88
89    pub fn confirmation_count(&self) -> u32 {
90        self.confirmation_count
91    }
92
93    pub fn increase_confirmation_count(&mut self, by: u32) {
94        self.confirmation_count = self.confirmation_count.saturating_add(by)
95    }
96}
97
98#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
99#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
100#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
101#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
102pub struct LandedVote {
103    // Latency is the difference in slot number between the slot that was voted on (lockout.slot) and the slot in
104    // which the vote that added this Lockout landed.  For votes which were cast before versions of the validator
105    // software which recorded vote latencies, latency is recorded as 0.
106    pub latency: u8,
107    pub lockout: Lockout,
108}
109
110impl LandedVote {
111    pub fn slot(&self) -> Slot {
112        self.lockout.slot
113    }
114
115    pub fn confirmation_count(&self) -> u32 {
116        self.lockout.confirmation_count
117    }
118}
119
120impl From<LandedVote> for Lockout {
121    fn from(landed_vote: LandedVote) -> Self {
122        landed_vote.lockout
123    }
124}
125
126impl From<Lockout> for LandedVote {
127    fn from(lockout: Lockout) -> Self {
128        Self {
129            latency: 0,
130            lockout,
131        }
132    }
133}
134
135#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
136#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
137#[derive(Debug, Default, PartialEq, Eq, Clone)]
138#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
139pub struct BlockTimestamp {
140    pub slot: Slot,
141    pub timestamp: UnixTimestamp,
142}
143
144// this is how many epochs a voter can be remembered for slashing
145const MAX_ITEMS: usize = 32;
146
147#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
148#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
149#[derive(Debug, PartialEq, Eq, Clone)]
150#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
151pub struct CircBuf<I> {
152    buf: [I; MAX_ITEMS],
153    /// next pointer
154    idx: usize,
155    is_empty: bool,
156}
157
158impl<I: Default + Copy> Default for CircBuf<I> {
159    fn default() -> Self {
160        Self {
161            buf: [I::default(); MAX_ITEMS],
162            idx: MAX_ITEMS
163                .checked_sub(1)
164                .expect("`MAX_ITEMS` should be positive"),
165            is_empty: true,
166        }
167    }
168}
169
170impl<I> CircBuf<I> {
171    pub fn append(&mut self, item: I) {
172        // remember prior delegate and when we switched, to support later slashing
173        self.idx = self
174            .idx
175            .checked_add(1)
176            .and_then(|idx| idx.checked_rem(MAX_ITEMS))
177            .expect("`self.idx` should be < `MAX_ITEMS` which should be non-zero");
178
179        self.buf[self.idx] = item;
180        self.is_empty = false;
181    }
182
183    pub fn buf(&self) -> &[I; MAX_ITEMS] {
184        &self.buf
185    }
186
187    pub fn last(&self) -> Option<&I> {
188        if !self.is_empty {
189            self.buf.get(self.idx)
190        } else {
191            None
192        }
193    }
194}
195
196#[cfg(feature = "serde")]
197pub mod serde_compact_vote_state_update {
198    use {
199        super::*,
200        crate::state::Lockout,
201        serde::{Deserialize, Deserializer, Serialize, Serializer},
202        solana_hash::Hash,
203        solana_serde_varint as serde_varint, solana_short_vec as short_vec,
204    };
205
206    #[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
207    #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
208    struct LockoutOffset {
209        #[serde(with = "serde_varint")]
210        offset: Slot,
211        confirmation_count: u8,
212    }
213
214    #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
215    struct CompactVoteStateUpdate {
216        root: Slot,
217        #[serde(with = "short_vec")]
218        lockout_offsets: Vec<LockoutOffset>,
219        hash: Hash,
220        timestamp: Option<UnixTimestamp>,
221    }
222
223    pub fn serialize<S>(
224        vote_state_update: &VoteStateUpdate,
225        serializer: S,
226    ) -> Result<S::Ok, S::Error>
227    where
228        S: Serializer,
229    {
230        let lockout_offsets = vote_state_update.lockouts.iter().scan(
231            vote_state_update.root.unwrap_or_default(),
232            |slot, lockout| {
233                let Some(offset) = lockout.slot().checked_sub(*slot) else {
234                    return Some(Err(serde::ser::Error::custom("Invalid vote lockout")));
235                };
236                let Ok(confirmation_count) = u8::try_from(lockout.confirmation_count()) else {
237                    return Some(Err(serde::ser::Error::custom("Invalid confirmation count")));
238                };
239                let lockout_offset = LockoutOffset {
240                    offset,
241                    confirmation_count,
242                };
243                *slot = lockout.slot();
244                Some(Ok(lockout_offset))
245            },
246        );
247        let compact_vote_state_update = CompactVoteStateUpdate {
248            root: vote_state_update.root.unwrap_or(Slot::MAX),
249            lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
250            hash: vote_state_update.hash,
251            timestamp: vote_state_update.timestamp,
252        };
253        compact_vote_state_update.serialize(serializer)
254    }
255
256    pub fn deserialize<'de, D>(deserializer: D) -> Result<VoteStateUpdate, D::Error>
257    where
258        D: Deserializer<'de>,
259    {
260        let CompactVoteStateUpdate {
261            root,
262            lockout_offsets,
263            hash,
264            timestamp,
265        } = CompactVoteStateUpdate::deserialize(deserializer)?;
266        let root = (root != Slot::MAX).then_some(root);
267        let lockouts =
268            lockout_offsets
269                .iter()
270                .scan(root.unwrap_or_default(), |slot, lockout_offset| {
271                    *slot = match slot.checked_add(lockout_offset.offset) {
272                        None => {
273                            return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
274                        }
275                        Some(slot) => slot,
276                    };
277                    let lockout = Lockout::new_with_confirmation_count(
278                        *slot,
279                        u32::from(lockout_offset.confirmation_count),
280                    );
281                    Some(Ok(lockout))
282                });
283        Ok(VoteStateUpdate {
284            root,
285            lockouts: lockouts.collect::<Result<_, _>>()?,
286            hash,
287            timestamp,
288        })
289    }
290}
291
292#[cfg(feature = "serde")]
293pub mod serde_tower_sync {
294    use {
295        super::*,
296        crate::state::Lockout,
297        serde::{Deserialize, Deserializer, Serialize, Serializer},
298        solana_hash::Hash,
299        solana_serde_varint as serde_varint, solana_short_vec as short_vec,
300    };
301
302    #[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
303    #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
304    struct LockoutOffset {
305        #[serde(with = "serde_varint")]
306        offset: Slot,
307        confirmation_count: u8,
308    }
309
310    #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
311    struct CompactTowerSync {
312        root: Slot,
313        #[serde(with = "short_vec")]
314        lockout_offsets: Vec<LockoutOffset>,
315        hash: Hash,
316        timestamp: Option<UnixTimestamp>,
317        block_id: Hash,
318    }
319
320    pub fn serialize<S>(tower_sync: &TowerSync, serializer: S) -> Result<S::Ok, S::Error>
321    where
322        S: Serializer,
323    {
324        let lockout_offsets = tower_sync.lockouts.iter().scan(
325            tower_sync.root.unwrap_or_default(),
326            |slot, lockout| {
327                let Some(offset) = lockout.slot().checked_sub(*slot) else {
328                    return Some(Err(serde::ser::Error::custom("Invalid vote lockout")));
329                };
330                let Ok(confirmation_count) = u8::try_from(lockout.confirmation_count()) else {
331                    return Some(Err(serde::ser::Error::custom("Invalid confirmation count")));
332                };
333                let lockout_offset = LockoutOffset {
334                    offset,
335                    confirmation_count,
336                };
337                *slot = lockout.slot();
338                Some(Ok(lockout_offset))
339            },
340        );
341        let compact_tower_sync = CompactTowerSync {
342            root: tower_sync.root.unwrap_or(Slot::MAX),
343            lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
344            hash: tower_sync.hash,
345            timestamp: tower_sync.timestamp,
346            block_id: tower_sync.block_id,
347        };
348        compact_tower_sync.serialize(serializer)
349    }
350
351    pub fn deserialize<'de, D>(deserializer: D) -> Result<TowerSync, D::Error>
352    where
353        D: Deserializer<'de>,
354    {
355        let CompactTowerSync {
356            root,
357            lockout_offsets,
358            hash,
359            timestamp,
360            block_id,
361        } = CompactTowerSync::deserialize(deserializer)?;
362        let root = (root != Slot::MAX).then_some(root);
363        let lockouts =
364            lockout_offsets
365                .iter()
366                .scan(root.unwrap_or_default(), |slot, lockout_offset| {
367                    *slot = match slot.checked_add(lockout_offset.offset) {
368                        None => {
369                            return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
370                        }
371                        Some(slot) => slot,
372                    };
373                    let lockout = Lockout::new_with_confirmation_count(
374                        *slot,
375                        u32::from(lockout_offset.confirmation_count),
376                    );
377                    Some(Ok(lockout))
378                });
379        Ok(TowerSync {
380            root,
381            lockouts: lockouts.collect::<Result<_, _>>()?,
382            hash,
383            timestamp,
384            block_id,
385        })
386    }
387}
388
389#[cfg(test)]
390mod tests {
391    use {
392        super::*, crate::error::VoteError, bincode::serialized_size, core::mem::MaybeUninit,
393        itertools::Itertools, rand::Rng, solana_clock::Clock, solana_hash::Hash,
394        solana_instruction::error::InstructionError,
395    };
396
397    #[test]
398    fn test_vote_serialize() {
399        let mut buffer: Vec<u8> = vec![0; VoteState::size_of()];
400        let mut vote_state = VoteState::default();
401        vote_state
402            .votes
403            .resize(MAX_LOCKOUT_HISTORY, LandedVote::default());
404        vote_state.root_slot = Some(1);
405        let versioned = VoteStateVersions::new_current(vote_state);
406        assert!(VoteState::serialize(&versioned, &mut buffer[0..4]).is_err());
407        VoteState::serialize(&versioned, &mut buffer).unwrap();
408        assert_eq!(
409            VoteState::deserialize(&buffer).unwrap(),
410            versioned.convert_to_current()
411        );
412    }
413
414    #[test]
415    fn test_vote_deserialize_into() {
416        // base case
417        let target_vote_state = VoteState::default();
418        let vote_state_buf =
419            bincode::serialize(&VoteStateVersions::new_current(target_vote_state.clone())).unwrap();
420
421        let mut test_vote_state = VoteState::default();
422        VoteState::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
423
424        assert_eq!(target_vote_state, test_vote_state);
425
426        // variant
427        // provide 4x the minimum struct size in bytes to ensure we typically touch every field
428        let struct_bytes_x4 = std::mem::size_of::<VoteState>() * 4;
429        for _ in 0..1000 {
430            let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
431            let mut unstructured = Unstructured::new(&raw_data);
432
433            let target_vote_state_versions =
434                VoteStateVersions::arbitrary(&mut unstructured).unwrap();
435            let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
436            let target_vote_state = target_vote_state_versions.convert_to_current();
437
438            let mut test_vote_state = VoteState::default();
439            VoteState::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
440
441            assert_eq!(target_vote_state, test_vote_state);
442        }
443    }
444
445    #[test]
446    fn test_vote_deserialize_into_error() {
447        let target_vote_state = VoteState::new_rand_for_tests(Pubkey::new_unique(), 42);
448        let mut vote_state_buf =
449            bincode::serialize(&VoteStateVersions::new_current(target_vote_state.clone())).unwrap();
450        let len = vote_state_buf.len();
451        vote_state_buf.truncate(len - 1);
452
453        let mut test_vote_state = VoteState::default();
454        VoteState::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap_err();
455        assert_eq!(test_vote_state, VoteState::default());
456    }
457
458    #[test]
459    fn test_vote_deserialize_into_uninit() {
460        // base case
461        let target_vote_state = VoteState::default();
462        let vote_state_buf =
463            bincode::serialize(&VoteStateVersions::new_current(target_vote_state.clone())).unwrap();
464
465        let mut test_vote_state = MaybeUninit::uninit();
466        VoteState::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state).unwrap();
467        let test_vote_state = unsafe { test_vote_state.assume_init() };
468
469        assert_eq!(target_vote_state, test_vote_state);
470
471        // variant
472        // provide 4x the minimum struct size in bytes to ensure we typically touch every field
473        let struct_bytes_x4 = std::mem::size_of::<VoteState>() * 4;
474        for _ in 0..1000 {
475            let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
476            let mut unstructured = Unstructured::new(&raw_data);
477
478            let target_vote_state_versions =
479                VoteStateVersions::arbitrary(&mut unstructured).unwrap();
480            let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
481            let target_vote_state = target_vote_state_versions.convert_to_current();
482
483            let mut test_vote_state = MaybeUninit::uninit();
484            VoteState::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state).unwrap();
485            let test_vote_state = unsafe { test_vote_state.assume_init() };
486
487            assert_eq!(target_vote_state, test_vote_state);
488        }
489    }
490
491    #[test]
492    fn test_vote_deserialize_into_uninit_nopanic() {
493        // base case
494        let mut test_vote_state = MaybeUninit::uninit();
495        let e = VoteState::deserialize_into_uninit(&[], &mut test_vote_state).unwrap_err();
496        assert_eq!(e, InstructionError::InvalidAccountData);
497
498        // variant
499        let serialized_len_x4 = serialized_size(&VoteState::default()).unwrap() * 4;
500        let mut rng = rand::thread_rng();
501        for _ in 0..1000 {
502            let raw_data_length = rng.gen_range(1..serialized_len_x4);
503            let mut raw_data: Vec<u8> = (0..raw_data_length).map(|_| rng.gen::<u8>()).collect();
504
505            // pure random data will ~never have a valid enum tag, so lets help it out
506            if raw_data_length >= 4 && rng.gen::<bool>() {
507                let tag = rng.gen::<u8>() % 3;
508                raw_data[0] = tag;
509                raw_data[1] = 0;
510                raw_data[2] = 0;
511                raw_data[3] = 0;
512            }
513
514            // it is extremely improbable, though theoretically possible, for random bytes to be syntactically valid
515            // so we only check that the parser does not panic and that it succeeds or fails exactly in line with bincode
516            let mut test_vote_state = MaybeUninit::uninit();
517            let test_res = VoteState::deserialize_into_uninit(&raw_data, &mut test_vote_state);
518            let bincode_res = bincode::deserialize::<VoteStateVersions>(&raw_data)
519                .map(|versioned| versioned.convert_to_current());
520
521            if test_res.is_err() {
522                assert!(bincode_res.is_err());
523            } else {
524                let test_vote_state = unsafe { test_vote_state.assume_init() };
525                assert_eq!(test_vote_state, bincode_res.unwrap());
526            }
527        }
528    }
529
530    #[test]
531    fn test_vote_deserialize_into_uninit_ill_sized() {
532        // provide 4x the minimum struct size in bytes to ensure we typically touch every field
533        let struct_bytes_x4 = std::mem::size_of::<VoteState>() * 4;
534        for _ in 0..1000 {
535            let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
536            let mut unstructured = Unstructured::new(&raw_data);
537
538            let original_vote_state_versions =
539                VoteStateVersions::arbitrary(&mut unstructured).unwrap();
540            let original_buf = bincode::serialize(&original_vote_state_versions).unwrap();
541
542            let mut truncated_buf = original_buf.clone();
543            let mut expanded_buf = original_buf.clone();
544
545            truncated_buf.resize(original_buf.len() - 8, 0);
546            expanded_buf.resize(original_buf.len() + 8, 0);
547
548            // truncated fails
549            let mut test_vote_state = MaybeUninit::uninit();
550            let test_res = VoteState::deserialize_into_uninit(&truncated_buf, &mut test_vote_state);
551            let bincode_res = bincode::deserialize::<VoteStateVersions>(&truncated_buf)
552                .map(|versioned| versioned.convert_to_current());
553
554            assert!(test_res.is_err());
555            assert!(bincode_res.is_err());
556
557            // expanded succeeds
558            let mut test_vote_state = MaybeUninit::uninit();
559            VoteState::deserialize_into_uninit(&expanded_buf, &mut test_vote_state).unwrap();
560            let bincode_res = bincode::deserialize::<VoteStateVersions>(&expanded_buf)
561                .map(|versioned| versioned.convert_to_current());
562
563            let test_vote_state = unsafe { test_vote_state.assume_init() };
564            assert_eq!(test_vote_state, bincode_res.unwrap());
565        }
566    }
567
568    #[test]
569    #[allow(deprecated)]
570    fn test_vote_state_commission_split() {
571        let vote_state = VoteState::default();
572
573        assert_eq!(vote_state.commission_split(1), (0, 1, false));
574
575        let mut vote_state = VoteState {
576            commission: u8::MAX,
577            ..VoteState::default()
578        };
579        assert_eq!(vote_state.commission_split(1), (1, 0, false));
580
581        vote_state.commission = 99;
582        assert_eq!(vote_state.commission_split(10), (9, 0, true));
583
584        vote_state.commission = 1;
585        assert_eq!(vote_state.commission_split(10), (0, 9, true));
586
587        vote_state.commission = 50;
588        let (voter_portion, staker_portion, was_split) = vote_state.commission_split(10);
589
590        assert_eq!((voter_portion, staker_portion, was_split), (5, 5, true));
591    }
592
593    #[test]
594    fn test_vote_state_epoch_credits() {
595        let mut vote_state = VoteState::default();
596
597        assert_eq!(vote_state.credits(), 0);
598        assert_eq!(vote_state.epoch_credits().clone(), vec![]);
599
600        let mut expected = vec![];
601        let mut credits = 0;
602        let epochs = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
603        for epoch in 0..epochs {
604            for _j in 0..epoch {
605                vote_state.increment_credits(epoch, 1);
606                credits += 1;
607            }
608            expected.push((epoch, credits, credits - epoch));
609        }
610
611        while expected.len() > MAX_EPOCH_CREDITS_HISTORY {
612            expected.remove(0);
613        }
614
615        assert_eq!(vote_state.credits(), credits);
616        assert_eq!(vote_state.epoch_credits().clone(), expected);
617    }
618
619    #[test]
620    fn test_vote_state_epoch0_no_credits() {
621        let mut vote_state = VoteState::default();
622
623        assert_eq!(vote_state.epoch_credits().len(), 0);
624        vote_state.increment_credits(1, 1);
625        assert_eq!(vote_state.epoch_credits().len(), 1);
626
627        vote_state.increment_credits(2, 1);
628        assert_eq!(vote_state.epoch_credits().len(), 2);
629    }
630
631    #[test]
632    fn test_vote_state_increment_credits() {
633        let mut vote_state = VoteState::default();
634
635        let credits = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
636        for i in 0..credits {
637            vote_state.increment_credits(i, 1);
638        }
639        assert_eq!(vote_state.credits(), credits);
640        assert!(vote_state.epoch_credits().len() <= MAX_EPOCH_CREDITS_HISTORY);
641    }
642
643    #[test]
644    fn test_vote_process_timestamp() {
645        let (slot, timestamp) = (15, 1_575_412_285);
646        let mut vote_state = VoteState {
647            last_timestamp: BlockTimestamp { slot, timestamp },
648            ..VoteState::default()
649        };
650
651        assert_eq!(
652            vote_state.process_timestamp(slot - 1, timestamp + 1),
653            Err(VoteError::TimestampTooOld)
654        );
655        assert_eq!(
656            vote_state.last_timestamp,
657            BlockTimestamp { slot, timestamp }
658        );
659        assert_eq!(
660            vote_state.process_timestamp(slot + 1, timestamp - 1),
661            Err(VoteError::TimestampTooOld)
662        );
663        assert_eq!(
664            vote_state.process_timestamp(slot, timestamp + 1),
665            Err(VoteError::TimestampTooOld)
666        );
667        assert_eq!(vote_state.process_timestamp(slot, timestamp), Ok(()));
668        assert_eq!(
669            vote_state.last_timestamp,
670            BlockTimestamp { slot, timestamp }
671        );
672        assert_eq!(vote_state.process_timestamp(slot + 1, timestamp), Ok(()));
673        assert_eq!(
674            vote_state.last_timestamp,
675            BlockTimestamp {
676                slot: slot + 1,
677                timestamp
678            }
679        );
680        assert_eq!(
681            vote_state.process_timestamp(slot + 2, timestamp + 1),
682            Ok(())
683        );
684        assert_eq!(
685            vote_state.last_timestamp,
686            BlockTimestamp {
687                slot: slot + 2,
688                timestamp: timestamp + 1
689            }
690        );
691
692        // Test initial vote
693        vote_state.last_timestamp = BlockTimestamp::default();
694        assert_eq!(vote_state.process_timestamp(0, timestamp), Ok(()));
695    }
696
697    #[test]
698    fn test_get_and_update_authorized_voter() {
699        let original_voter = Pubkey::new_unique();
700        let mut vote_state = VoteState::new(
701            &VoteInit {
702                node_pubkey: original_voter,
703                authorized_voter: original_voter,
704                authorized_withdrawer: original_voter,
705                commission: 0,
706            },
707            &Clock::default(),
708        );
709
710        assert_eq!(vote_state.authorized_voters.len(), 1);
711        assert_eq!(
712            *vote_state.authorized_voters.first().unwrap().1,
713            original_voter
714        );
715
716        // If no new authorized voter was set, the same authorized voter
717        // is locked into the next epoch
718        assert_eq!(
719            vote_state.get_and_update_authorized_voter(1).unwrap(),
720            original_voter
721        );
722
723        // Try to get the authorized voter for epoch 5, implies
724        // the authorized voter for epochs 1-4 were unchanged
725        assert_eq!(
726            vote_state.get_and_update_authorized_voter(5).unwrap(),
727            original_voter
728        );
729
730        // Authorized voter for expired epoch 0..5 should have been
731        // purged and no longer queryable
732        assert_eq!(vote_state.authorized_voters.len(), 1);
733        for i in 0..5 {
734            assert!(vote_state
735                .authorized_voters
736                .get_authorized_voter(i)
737                .is_none());
738        }
739
740        // Set an authorized voter change at slot 7
741        let new_authorized_voter = Pubkey::new_unique();
742        vote_state
743            .set_new_authorized_voter(&new_authorized_voter, 5, 7, |_| Ok(()))
744            .unwrap();
745
746        // Try to get the authorized voter for epoch 6, unchanged
747        assert_eq!(
748            vote_state.get_and_update_authorized_voter(6).unwrap(),
749            original_voter
750        );
751
752        // Try to get the authorized voter for epoch 7 and onwards, should
753        // be the new authorized voter
754        for i in 7..10 {
755            assert_eq!(
756                vote_state.get_and_update_authorized_voter(i).unwrap(),
757                new_authorized_voter
758            );
759        }
760        assert_eq!(vote_state.authorized_voters.len(), 1);
761    }
762
763    #[test]
764    fn test_set_new_authorized_voter() {
765        let original_voter = Pubkey::new_unique();
766        let epoch_offset = 15;
767        let mut vote_state = VoteState::new(
768            &VoteInit {
769                node_pubkey: original_voter,
770                authorized_voter: original_voter,
771                authorized_withdrawer: original_voter,
772                commission: 0,
773            },
774            &Clock::default(),
775        );
776
777        assert!(vote_state.prior_voters.last().is_none());
778
779        let new_voter = Pubkey::new_unique();
780        // Set a new authorized voter
781        vote_state
782            .set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(()))
783            .unwrap();
784
785        assert_eq!(vote_state.prior_voters.idx, 0);
786        assert_eq!(
787            vote_state.prior_voters.last(),
788            Some(&(original_voter, 0, epoch_offset))
789        );
790
791        // Trying to set authorized voter for same epoch again should fail
792        assert_eq!(
793            vote_state.set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(())),
794            Err(VoteError::TooSoonToReauthorize.into())
795        );
796
797        // Setting the same authorized voter again should succeed
798        vote_state
799            .set_new_authorized_voter(&new_voter, 2, 2 + epoch_offset, |_| Ok(()))
800            .unwrap();
801
802        // Set a third and fourth authorized voter
803        let new_voter2 = Pubkey::new_unique();
804        vote_state
805            .set_new_authorized_voter(&new_voter2, 3, 3 + epoch_offset, |_| Ok(()))
806            .unwrap();
807        assert_eq!(vote_state.prior_voters.idx, 1);
808        assert_eq!(
809            vote_state.prior_voters.last(),
810            Some(&(new_voter, epoch_offset, 3 + epoch_offset))
811        );
812
813        let new_voter3 = Pubkey::new_unique();
814        vote_state
815            .set_new_authorized_voter(&new_voter3, 6, 6 + epoch_offset, |_| Ok(()))
816            .unwrap();
817        assert_eq!(vote_state.prior_voters.idx, 2);
818        assert_eq!(
819            vote_state.prior_voters.last(),
820            Some(&(new_voter2, 3 + epoch_offset, 6 + epoch_offset))
821        );
822
823        // Check can set back to original voter
824        vote_state
825            .set_new_authorized_voter(&original_voter, 9, 9 + epoch_offset, |_| Ok(()))
826            .unwrap();
827
828        // Run with these voters for a while, check the ranges of authorized
829        // voters is correct
830        for i in 9..epoch_offset {
831            assert_eq!(
832                vote_state.get_and_update_authorized_voter(i).unwrap(),
833                original_voter
834            );
835        }
836        for i in epoch_offset..3 + epoch_offset {
837            assert_eq!(
838                vote_state.get_and_update_authorized_voter(i).unwrap(),
839                new_voter
840            );
841        }
842        for i in 3 + epoch_offset..6 + epoch_offset {
843            assert_eq!(
844                vote_state.get_and_update_authorized_voter(i).unwrap(),
845                new_voter2
846            );
847        }
848        for i in 6 + epoch_offset..9 + epoch_offset {
849            assert_eq!(
850                vote_state.get_and_update_authorized_voter(i).unwrap(),
851                new_voter3
852            );
853        }
854        for i in 9 + epoch_offset..=10 + epoch_offset {
855            assert_eq!(
856                vote_state.get_and_update_authorized_voter(i).unwrap(),
857                original_voter
858            );
859        }
860    }
861
862    #[test]
863    fn test_authorized_voter_is_locked_within_epoch() {
864        let original_voter = Pubkey::new_unique();
865        let mut vote_state = VoteState::new(
866            &VoteInit {
867                node_pubkey: original_voter,
868                authorized_voter: original_voter,
869                authorized_withdrawer: original_voter,
870                commission: 0,
871            },
872            &Clock::default(),
873        );
874
875        // Test that it's not possible to set a new authorized
876        // voter within the same epoch, even if none has been
877        // explicitly set before
878        let new_voter = Pubkey::new_unique();
879        assert_eq!(
880            vote_state.set_new_authorized_voter(&new_voter, 1, 1, |_| Ok(())),
881            Err(VoteError::TooSoonToReauthorize.into())
882        );
883
884        assert_eq!(vote_state.get_authorized_voter(1), Some(original_voter));
885
886        // Set a new authorized voter for a future epoch
887        assert_eq!(
888            vote_state.set_new_authorized_voter(&new_voter, 1, 2, |_| Ok(())),
889            Ok(())
890        );
891
892        // Test that it's not possible to set a new authorized
893        // voter within the same epoch, even if none has been
894        // explicitly set before
895        assert_eq!(
896            vote_state.set_new_authorized_voter(&original_voter, 3, 3, |_| Ok(())),
897            Err(VoteError::TooSoonToReauthorize.into())
898        );
899
900        assert_eq!(vote_state.get_authorized_voter(3), Some(new_voter));
901    }
902
903    #[test]
904    fn test_vote_state_size_of() {
905        let vote_state = VoteState::get_max_sized_vote_state();
906        let vote_state = VoteStateVersions::new_current(vote_state);
907        let size = serialized_size(&vote_state).unwrap();
908        assert_eq!(VoteState::size_of() as u64, size);
909    }
910
911    #[test]
912    fn test_vote_state_max_size() {
913        let mut max_sized_data = vec![0; VoteState::size_of()];
914        let vote_state = VoteState::get_max_sized_vote_state();
915        let (start_leader_schedule_epoch, _) = vote_state.authorized_voters.last().unwrap();
916        let start_current_epoch =
917            start_leader_schedule_epoch - MAX_LEADER_SCHEDULE_EPOCH_OFFSET + 1;
918
919        let mut vote_state = Some(vote_state);
920        for i in start_current_epoch..start_current_epoch + 2 * MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
921            vote_state.as_mut().map(|vote_state| {
922                vote_state.set_new_authorized_voter(
923                    &Pubkey::new_unique(),
924                    i,
925                    i + MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
926                    |_| Ok(()),
927                )
928            });
929
930            let versioned = VoteStateVersions::new_current(vote_state.take().unwrap());
931            VoteState::serialize(&versioned, &mut max_sized_data).unwrap();
932            vote_state = Some(versioned.convert_to_current());
933        }
934    }
935
936    #[test]
937    fn test_default_vote_state_is_uninitialized() {
938        // The default `VoteState` is stored to de-initialize a zero-balance vote account,
939        // so must remain such that `VoteStateVersions::is_uninitialized()` returns true
940        // when called on a `VoteStateVersions` that stores it
941        assert!(VoteStateVersions::new_current(VoteState::default()).is_uninitialized());
942    }
943
944    #[test]
945    fn test_is_correct_size_and_initialized() {
946        // Check all zeroes
947        let mut vote_account_data = vec![0; VoteStateVersions::vote_state_size_of(true)];
948        assert!(!VoteStateVersions::is_correct_size_and_initialized(
949            &vote_account_data
950        ));
951
952        // Check default VoteState
953        let default_account_state = VoteStateVersions::new_current(VoteState::default());
954        VoteState::serialize(&default_account_state, &mut vote_account_data).unwrap();
955        assert!(!VoteStateVersions::is_correct_size_and_initialized(
956            &vote_account_data
957        ));
958
959        // Check non-zero data shorter than offset index used
960        let short_data = vec![1; DEFAULT_PRIOR_VOTERS_OFFSET];
961        assert!(!VoteStateVersions::is_correct_size_and_initialized(
962            &short_data
963        ));
964
965        // Check non-zero large account
966        let mut large_vote_data = vec![1; 2 * VoteStateVersions::vote_state_size_of(true)];
967        let default_account_state = VoteStateVersions::new_current(VoteState::default());
968        VoteState::serialize(&default_account_state, &mut large_vote_data).unwrap();
969        assert!(!VoteStateVersions::is_correct_size_and_initialized(
970            &vote_account_data
971        ));
972
973        // Check populated VoteState
974        let vote_state = VoteState::new(
975            &VoteInit {
976                node_pubkey: Pubkey::new_unique(),
977                authorized_voter: Pubkey::new_unique(),
978                authorized_withdrawer: Pubkey::new_unique(),
979                commission: 0,
980            },
981            &Clock::default(),
982        );
983        let account_state = VoteStateVersions::new_current(vote_state.clone());
984        VoteState::serialize(&account_state, &mut vote_account_data).unwrap();
985        assert!(VoteStateVersions::is_correct_size_and_initialized(
986            &vote_account_data
987        ));
988
989        // Check old VoteState that hasn't been upgraded to newest version yet
990        let old_vote_state = VoteState1_14_11::from(vote_state);
991        let account_state = VoteStateVersions::V1_14_11(Box::new(old_vote_state));
992        let mut vote_account_data = vec![0; VoteStateVersions::vote_state_size_of(false)];
993        VoteState::serialize(&account_state, &mut vote_account_data).unwrap();
994        assert!(VoteStateVersions::is_correct_size_and_initialized(
995            &vote_account_data
996        ));
997    }
998
999    #[test]
1000    fn test_minimum_balance() {
1001        let rent = solana_rent::Rent::default();
1002        let minimum_balance = rent.minimum_balance(VoteState::size_of());
1003        // golden, may need updating when vote_state grows
1004        assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)
1005    }
1006
1007    #[test]
1008    fn test_serde_compact_vote_state_update() {
1009        let mut rng = rand::thread_rng();
1010        for _ in 0..5000 {
1011            run_serde_compact_vote_state_update(&mut rng);
1012        }
1013    }
1014
1015    fn run_serde_compact_vote_state_update<R: Rng>(rng: &mut R) {
1016        let lockouts: VecDeque<_> = std::iter::repeat_with(|| {
1017            let slot = 149_303_885_u64.saturating_add(rng.gen_range(0..10_000));
1018            let confirmation_count = rng.gen_range(0..33);
1019            Lockout::new_with_confirmation_count(slot, confirmation_count)
1020        })
1021        .take(32)
1022        .sorted_by_key(|lockout| lockout.slot())
1023        .collect();
1024        let root = rng.gen_ratio(1, 2).then(|| {
1025            lockouts[0]
1026                .slot()
1027                .checked_sub(rng.gen_range(0..1_000))
1028                .expect("All slots should be greater than 1_000")
1029        });
1030        let timestamp = rng.gen_ratio(1, 2).then(|| rng.gen());
1031        let hash = Hash::from(rng.gen::<[u8; 32]>());
1032        let vote_state_update = VoteStateUpdate {
1033            lockouts,
1034            root,
1035            hash,
1036            timestamp,
1037        };
1038        #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
1039        enum VoteInstruction {
1040            #[serde(with = "serde_compact_vote_state_update")]
1041            UpdateVoteState(VoteStateUpdate),
1042            UpdateVoteStateSwitch(
1043                #[serde(with = "serde_compact_vote_state_update")] VoteStateUpdate,
1044                Hash,
1045            ),
1046        }
1047        let vote = VoteInstruction::UpdateVoteState(vote_state_update.clone());
1048        let bytes = bincode::serialize(&vote).unwrap();
1049        assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1050        let hash = Hash::from(rng.gen::<[u8; 32]>());
1051        let vote = VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash);
1052        let bytes = bincode::serialize(&vote).unwrap();
1053        assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1054    }
1055
1056    #[test]
1057    fn test_circbuf_oob() {
1058        // Craft an invalid CircBuf with out-of-bounds index
1059        let data: &[u8] = &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00];
1060        let circ_buf: CircBuf<()> = bincode::deserialize(data).unwrap();
1061        assert_eq!(circ_buf.last(), None);
1062    }
1063}