solana_vote_interface/state/
mod.rs

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