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