Skip to main content

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