miraland_program/vote/state/
mod.rs

1//! Vote state
2
3#[cfg(not(target_os = "solana"))]
4use bincode::deserialize;
5#[cfg(test)]
6use {
7    crate::epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
8    arbitrary::{Arbitrary, Unstructured},
9};
10use {
11    crate::{
12        clock::{Epoch, Slot, UnixTimestamp},
13        hash::Hash,
14        instruction::InstructionError,
15        pubkey::Pubkey,
16        rent::Rent,
17        serialize_utils::cursor::read_u32,
18        sysvar::clock::Clock,
19        vote::{authorized_voters::AuthorizedVoters, error::VoteError},
20    },
21    bincode::{serialize_into, serialized_size, ErrorKind},
22    serde_derive::{Deserialize, Serialize},
23    std::{collections::VecDeque, fmt::Debug, io::Cursor},
24};
25
26mod vote_state_0_23_5;
27pub mod vote_state_1_14_11;
28pub use vote_state_1_14_11::*;
29mod vote_state_deserialize;
30use vote_state_deserialize::deserialize_vote_state_into;
31pub mod vote_state_versions;
32pub use vote_state_versions::*;
33
34// Maximum number of votes to keep around, tightly coupled with epoch_schedule::MINIMUM_SLOTS_PER_EPOCH
35pub const MAX_LOCKOUT_HISTORY: usize = 31;
36pub const INITIAL_LOCKOUT: usize = 2;
37
38// Maximum number of credits history to keep around
39pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
40
41// Offset of VoteState::prior_voters, for determining initialization status without deserialization
42const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 114;
43
44// 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.
45pub const VOTE_CREDITS_GRACE_SLOTS: u8 = 2;
46
47// 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.
48pub const VOTE_CREDITS_MAXIMUM_PER_SLOT: u8 = 8;
49
50#[frozen_abi(digest = "Ch2vVEwos2EjAVqSHCyJjnN2MNX1yrpapZTGhMSCjWUH")]
51#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
52pub struct Vote {
53    /// A stack of votes starting with the oldest vote
54    pub slots: Vec<Slot>,
55    /// signature of the bank's state at the last slot
56    pub hash: Hash,
57    /// processing timestamp of last slot
58    pub timestamp: Option<UnixTimestamp>,
59}
60
61impl Vote {
62    pub fn new(slots: Vec<Slot>, hash: Hash) -> Self {
63        Self {
64            slots,
65            hash,
66            timestamp: None,
67        }
68    }
69
70    pub fn last_voted_slot(&self) -> Option<Slot> {
71        self.slots.last().copied()
72    }
73}
74
75#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Copy, Clone, AbiExample)]
76#[cfg_attr(test, derive(Arbitrary))]
77pub struct Lockout {
78    slot: Slot,
79    confirmation_count: u32,
80}
81
82impl Lockout {
83    pub fn new(slot: Slot) -> Self {
84        Self::new_with_confirmation_count(slot, 1)
85    }
86
87    pub fn new_with_confirmation_count(slot: Slot, confirmation_count: u32) -> Self {
88        Self {
89            slot,
90            confirmation_count,
91        }
92    }
93
94    // The number of slots for which this vote is locked
95    pub fn lockout(&self) -> u64 {
96        (INITIAL_LOCKOUT as u64).pow(self.confirmation_count())
97    }
98
99    // The last slot at which a vote is still locked out. Validators should not
100    // vote on a slot in another fork which is less than or equal to this slot
101    // to avoid having their stake slashed.
102    pub fn last_locked_out_slot(&self) -> Slot {
103        self.slot.saturating_add(self.lockout())
104    }
105
106    pub fn is_locked_out_at_slot(&self, slot: Slot) -> bool {
107        self.last_locked_out_slot() >= slot
108    }
109
110    pub fn slot(&self) -> Slot {
111        self.slot
112    }
113
114    pub fn confirmation_count(&self) -> u32 {
115        self.confirmation_count
116    }
117
118    pub fn increase_confirmation_count(&mut self, by: u32) {
119        self.confirmation_count = self.confirmation_count.saturating_add(by)
120    }
121}
122
123#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Copy, Clone, AbiExample)]
124#[cfg_attr(test, derive(Arbitrary))]
125pub struct LandedVote {
126    // Latency is the difference in slot number between the slot that was voted on (lockout.slot) and the slot in
127    // which the vote that added this Lockout landed.  For votes which were cast before versions of the validator
128    // software which recorded vote latencies, latency is recorded as 0.
129    pub latency: u8,
130    pub lockout: Lockout,
131}
132
133impl LandedVote {
134    pub fn slot(&self) -> Slot {
135        self.lockout.slot
136    }
137
138    pub fn confirmation_count(&self) -> u32 {
139        self.lockout.confirmation_count
140    }
141}
142
143impl From<LandedVote> for Lockout {
144    fn from(landed_vote: LandedVote) -> Self {
145        landed_vote.lockout
146    }
147}
148
149impl From<Lockout> for LandedVote {
150    fn from(lockout: Lockout) -> Self {
151        Self {
152            latency: 0,
153            lockout,
154        }
155    }
156}
157
158#[frozen_abi(digest = "GwJfVFsATSj7nvKwtUkHYzqPRaPY6SLxPGXApuCya3x5")]
159#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
160pub struct VoteStateUpdate {
161    /// The proposed tower
162    pub lockouts: VecDeque<Lockout>,
163    /// The proposed root
164    pub root: Option<Slot>,
165    /// signature of the bank's state at the last slot
166    pub hash: Hash,
167    /// processing timestamp of last slot
168    pub timestamp: Option<UnixTimestamp>,
169}
170
171impl From<Vec<(Slot, u32)>> for VoteStateUpdate {
172    fn from(recent_slots: Vec<(Slot, u32)>) -> Self {
173        let lockouts: VecDeque<Lockout> = recent_slots
174            .into_iter()
175            .map(|(slot, confirmation_count)| {
176                Lockout::new_with_confirmation_count(slot, confirmation_count)
177            })
178            .collect();
179        Self {
180            lockouts,
181            root: None,
182            hash: Hash::default(),
183            timestamp: None,
184        }
185    }
186}
187
188impl VoteStateUpdate {
189    pub fn new(lockouts: VecDeque<Lockout>, root: Option<Slot>, hash: Hash) -> Self {
190        Self {
191            lockouts,
192            root,
193            hash,
194            timestamp: None,
195        }
196    }
197
198    pub fn slots(&self) -> Vec<Slot> {
199        self.lockouts.iter().map(|lockout| lockout.slot()).collect()
200    }
201
202    pub fn last_voted_slot(&self) -> Option<Slot> {
203        self.lockouts.back().map(|l| l.slot())
204    }
205}
206
207#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
208pub struct VoteInit {
209    pub node_pubkey: Pubkey,
210    pub authorized_voter: Pubkey,
211    pub authorized_withdrawer: Pubkey,
212    pub commission: u8,
213}
214
215#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
216pub enum VoteAuthorize {
217    Voter,
218    Withdrawer,
219}
220
221#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
222pub struct VoteAuthorizeWithSeedArgs {
223    pub authorization_type: VoteAuthorize,
224    pub current_authority_derived_key_owner: Pubkey,
225    pub current_authority_derived_key_seed: String,
226    pub new_authority: Pubkey,
227}
228
229#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
230pub struct VoteAuthorizeCheckedWithSeedArgs {
231    pub authorization_type: VoteAuthorize,
232    pub current_authority_derived_key_owner: Pubkey,
233    pub current_authority_derived_key_seed: String,
234}
235
236#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
237#[cfg_attr(test, derive(Arbitrary))]
238pub struct BlockTimestamp {
239    pub slot: Slot,
240    pub timestamp: UnixTimestamp,
241}
242
243// this is how many epochs a voter can be remembered for slashing
244const MAX_ITEMS: usize = 32;
245
246#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
247pub struct CircBuf<I> {
248    buf: [I; MAX_ITEMS],
249    /// next pointer
250    idx: usize,
251    is_empty: bool,
252}
253
254impl<I: Default + Copy> Default for CircBuf<I> {
255    fn default() -> Self {
256        Self {
257            buf: [I::default(); MAX_ITEMS],
258            idx: MAX_ITEMS
259                .checked_sub(1)
260                .expect("`MAX_ITEMS` should be positive"),
261            is_empty: true,
262        }
263    }
264}
265
266impl<I> CircBuf<I> {
267    pub fn append(&mut self, item: I) {
268        // remember prior delegate and when we switched, to support later slashing
269        self.idx = self
270            .idx
271            .checked_add(1)
272            .and_then(|idx| idx.checked_rem(MAX_ITEMS))
273            .expect("`self.idx` should be < `MAX_ITEMS` which should be non-zero");
274
275        self.buf[self.idx] = item;
276        self.is_empty = false;
277    }
278
279    pub fn buf(&self) -> &[I; MAX_ITEMS] {
280        &self.buf
281    }
282
283    pub fn last(&self) -> Option<&I> {
284        if !self.is_empty {
285            Some(&self.buf[self.idx])
286        } else {
287            None
288        }
289    }
290}
291
292#[cfg(test)]
293impl<'a, I: Default + Copy> Arbitrary<'a> for CircBuf<I>
294where
295    I: Arbitrary<'a>,
296{
297    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
298        let mut circbuf = Self::default();
299
300        let len = u.arbitrary_len::<I>()?;
301        for _ in 0..len {
302            circbuf.append(I::arbitrary(u)?);
303        }
304
305        Ok(circbuf)
306    }
307}
308
309#[frozen_abi(digest = "EeenjJaSrm9hRM39gK6raRNtzG61hnk7GciUCJJRDUSQ")]
310#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
311#[cfg_attr(test, derive(Arbitrary))]
312pub struct VoteState {
313    /// the node that votes in this account
314    pub node_pubkey: Pubkey,
315
316    /// the signer for withdrawals
317    pub authorized_withdrawer: Pubkey,
318    /// percentage (0-100) that represents what part of a rewards
319    ///  payout should be given to this VoteAccount
320    pub commission: u8,
321
322    pub votes: VecDeque<LandedVote>,
323
324    // This usually the last Lockout which was popped from self.votes.
325    // However, it can be arbitrary slot, when being used inside Tower
326    pub root_slot: Option<Slot>,
327
328    /// the signer for vote transactions
329    authorized_voters: AuthorizedVoters,
330
331    /// history of prior authorized voters and the epochs for which
332    /// they were set, the bottom end of the range is inclusive,
333    /// the top of the range is exclusive
334    prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>,
335
336    /// history of how many credits earned by the end of each epoch
337    ///  each tuple is (Epoch, credits, prev_credits)
338    pub epoch_credits: Vec<(Epoch, u64, u64)>,
339
340    /// most recent timestamp submitted with a vote
341    pub last_timestamp: BlockTimestamp,
342}
343
344impl VoteState {
345    pub fn new(vote_init: &VoteInit, clock: &Clock) -> Self {
346        Self {
347            node_pubkey: vote_init.node_pubkey,
348            authorized_voters: AuthorizedVoters::new(clock.epoch, vote_init.authorized_voter),
349            authorized_withdrawer: vote_init.authorized_withdrawer,
350            commission: vote_init.commission,
351            ..VoteState::default()
352        }
353    }
354
355    pub fn new_rand_for_tests(node_pubkey: Pubkey, root_slot: Slot) -> Self {
356        let votes = (1..32)
357            .map(|x| LandedVote {
358                latency: 0,
359                lockout: Lockout::new_with_confirmation_count(
360                    u64::from(x).saturating_add(root_slot),
361                    32_u32.saturating_sub(x),
362                ),
363            })
364            .collect();
365        Self {
366            node_pubkey,
367            root_slot: Some(root_slot),
368            votes,
369            ..VoteState::default()
370        }
371    }
372
373    pub fn get_authorized_voter(&self, epoch: Epoch) -> Option<Pubkey> {
374        self.authorized_voters.get_authorized_voter(epoch)
375    }
376
377    pub fn authorized_voters(&self) -> &AuthorizedVoters {
378        &self.authorized_voters
379    }
380
381    pub fn prior_voters(&mut self) -> &CircBuf<(Pubkey, Epoch, Epoch)> {
382        &self.prior_voters
383    }
384
385    pub fn get_rent_exempt_reserve(rent: &Rent) -> u64 {
386        rent.minimum_balance(VoteState::size_of())
387    }
388
389    /// Upper limit on the size of the Vote State
390    /// when votes.len() is MAX_LOCKOUT_HISTORY.
391    pub const fn size_of() -> usize {
392        3762 // see test_vote_state_size_of.
393    }
394
395    // we retain bincode deserialize for not(target_os = "solana")
396    // because the hand-written parser does not support V0_23_5
397    pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
398        #[cfg(not(target_os = "solana"))]
399        {
400            deserialize::<VoteStateVersions>(input)
401                .map(|versioned| versioned.convert_to_current())
402                .map_err(|_| InstructionError::InvalidAccountData)
403        }
404        #[cfg(target_os = "solana")]
405        {
406            let mut vote_state = Self::default();
407            Self::deserialize_into(input, &mut vote_state)?;
408            Ok(vote_state)
409        }
410    }
411
412    /// Deserializes the input buffer into the provided `VoteState`
413    ///
414    /// This function exists to deserialize `VoteState` in a BPF context without going above
415    /// the compute limit, and must be kept up to date with `bincode::deserialize`.
416    pub fn deserialize_into(
417        input: &[u8],
418        vote_state: &mut VoteState,
419    ) -> Result<(), InstructionError> {
420        let minimum_size =
421            serialized_size(vote_state).map_err(|_| InstructionError::InvalidAccountData)?;
422        if (input.len() as u64) < minimum_size {
423            return Err(InstructionError::InvalidAccountData);
424        }
425
426        let mut cursor = Cursor::new(input);
427
428        let variant = read_u32(&mut cursor)?;
429        match variant {
430            // V0_23_5. not supported; these should not exist on mainnet
431            0 => Err(InstructionError::InvalidAccountData),
432            // V1_14_11. substantially different layout and data from V0_23_5
433            1 => deserialize_vote_state_into(&mut cursor, vote_state, false),
434            // Current. the only difference from V1_14_11 is the addition of a slot-latency to each vote
435            2 => deserialize_vote_state_into(&mut cursor, vote_state, true),
436            _ => Err(InstructionError::InvalidAccountData),
437        }?;
438
439        if cursor.position() > input.len() as u64 {
440            return Err(InstructionError::InvalidAccountData);
441        }
442
443        Ok(())
444    }
445
446    pub fn serialize(
447        versioned: &VoteStateVersions,
448        output: &mut [u8],
449    ) -> Result<(), InstructionError> {
450        serialize_into(output, versioned).map_err(|err| match *err {
451            ErrorKind::SizeLimit => InstructionError::AccountDataTooSmall,
452            _ => InstructionError::GenericError,
453        })
454    }
455
456    /// returns commission split as (voter_portion, staker_portion, was_split) tuple
457    ///
458    ///  if commission calculation is 100% one way or other,
459    ///   indicate with false for was_split
460    pub fn commission_split(&self, on: u64) -> (u64, u64, bool) {
461        match self.commission.min(100) {
462            0 => (0, on, false),
463            100 => (on, 0, false),
464            split => {
465                let on = u128::from(on);
466                // Calculate mine and theirs independently and symmetrically instead of
467                // using the remainder of the other to treat them strictly equally.
468                // This is also to cancel the rewarding if either of the parties
469                // should receive only fractional lamports, resulting in not being rewarded at all.
470                // Thus, note that we intentionally discard any residual fractional lamports.
471                let mine = on
472                    .checked_mul(u128::from(split))
473                    .expect("multiplication of a u64 and u8 should not overflow")
474                    / 100u128;
475                let theirs = on
476                    .checked_mul(u128::from(
477                        100u8
478                            .checked_sub(split)
479                            .expect("commission cannot be greater than 100"),
480                    ))
481                    .expect("multiplication of a u64 and u8 should not overflow")
482                    / 100u128;
483
484                (mine as u64, theirs as u64, true)
485            }
486        }
487    }
488
489    /// Returns if the vote state contains a slot `candidate_slot`
490    pub fn contains_slot(&self, candidate_slot: Slot) -> bool {
491        self.votes
492            .binary_search_by(|vote| vote.slot().cmp(&candidate_slot))
493            .is_ok()
494    }
495
496    #[cfg(test)]
497    fn get_max_sized_vote_state() -> VoteState {
498        let mut authorized_voters = AuthorizedVoters::default();
499        for i in 0..=MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
500            authorized_voters.insert(i, Pubkey::new_unique());
501        }
502
503        VoteState {
504            votes: VecDeque::from(vec![LandedVote::default(); MAX_LOCKOUT_HISTORY]),
505            root_slot: Some(std::u64::MAX),
506            epoch_credits: vec![(0, 0, 0); MAX_EPOCH_CREDITS_HISTORY],
507            authorized_voters,
508            ..Self::default()
509        }
510    }
511
512    pub fn process_next_vote_slot(
513        &mut self,
514        next_vote_slot: Slot,
515        epoch: Epoch,
516        current_slot: Slot,
517    ) {
518        // Ignore votes for slots earlier than we already have votes for
519        if self
520            .last_voted_slot()
521            .map_or(false, |last_voted_slot| next_vote_slot <= last_voted_slot)
522        {
523            return;
524        }
525
526        self.pop_expired_votes(next_vote_slot);
527
528        let landed_vote = LandedVote {
529            latency: Self::compute_vote_latency(next_vote_slot, current_slot),
530            lockout: Lockout::new(next_vote_slot),
531        };
532
533        // Once the stack is full, pop the oldest lockout and distribute rewards
534        if self.votes.len() == MAX_LOCKOUT_HISTORY {
535            let credits = self.credits_for_vote_at_index(0);
536            let landed_vote = self.votes.pop_front().unwrap();
537            self.root_slot = Some(landed_vote.slot());
538
539            self.increment_credits(epoch, credits);
540        }
541        self.votes.push_back(landed_vote);
542        self.double_lockouts();
543    }
544
545    /// increment credits, record credits for last epoch if new epoch
546    pub fn increment_credits(&mut self, epoch: Epoch, credits: u64) {
547        // increment credits, record by epoch
548
549        // never seen a credit
550        if self.epoch_credits.is_empty() {
551            self.epoch_credits.push((epoch, 0, 0));
552        } else if epoch != self.epoch_credits.last().unwrap().0 {
553            let (_, credits, prev_credits) = *self.epoch_credits.last().unwrap();
554
555            if credits != prev_credits {
556                // if credits were earned previous epoch
557                // append entry at end of list for the new epoch
558                self.epoch_credits.push((epoch, credits, credits));
559            } else {
560                // else just move the current epoch
561                self.epoch_credits.last_mut().unwrap().0 = epoch;
562            }
563
564            // Remove too old epoch_credits
565            if self.epoch_credits.len() > MAX_EPOCH_CREDITS_HISTORY {
566                self.epoch_credits.remove(0);
567            }
568        }
569
570        self.epoch_credits.last_mut().unwrap().1 =
571            self.epoch_credits.last().unwrap().1.saturating_add(credits);
572    }
573
574    // Computes the vote latency for vote on voted_for_slot where the vote itself landed in current_slot
575    pub fn compute_vote_latency(voted_for_slot: Slot, current_slot: Slot) -> u8 {
576        std::cmp::min(current_slot.saturating_sub(voted_for_slot), u8::MAX as u64) as u8
577    }
578
579    /// Returns the credits to award for a vote at the given lockout slot index
580    pub fn credits_for_vote_at_index(&self, index: usize) -> u64 {
581        let latency = self
582            .votes
583            .get(index)
584            .map_or(0, |landed_vote| landed_vote.latency);
585
586        // If latency is 0, this means that the Lockout was created and stored from a software version that did not
587        // store vote latencies; in this case, 1 credit is awarded
588        if latency == 0 {
589            1
590        } else {
591            match latency.checked_sub(VOTE_CREDITS_GRACE_SLOTS) {
592                None | Some(0) => {
593                    // latency was <= VOTE_CREDITS_GRACE_SLOTS, so maximum credits are awarded
594                    VOTE_CREDITS_MAXIMUM_PER_SLOT as u64
595                }
596
597                Some(diff) => {
598                    // diff = latency - VOTE_CREDITS_GRACE_SLOTS, and diff > 0
599                    // Subtract diff from VOTE_CREDITS_MAXIMUM_PER_SLOT which is the number of credits to award
600                    match VOTE_CREDITS_MAXIMUM_PER_SLOT.checked_sub(diff) {
601                        // If diff >= VOTE_CREDITS_MAXIMUM_PER_SLOT, 1 credit is awarded
602                        None | Some(0) => 1,
603
604                        Some(credits) => credits as u64,
605                    }
606                }
607            }
608        }
609    }
610
611    pub fn nth_recent_lockout(&self, position: usize) -> Option<&Lockout> {
612        if position < self.votes.len() {
613            let pos = self
614                .votes
615                .len()
616                .checked_sub(position)
617                .and_then(|pos| pos.checked_sub(1))?;
618            self.votes.get(pos).map(|vote| &vote.lockout)
619        } else {
620            None
621        }
622    }
623
624    pub fn last_lockout(&self) -> Option<&Lockout> {
625        self.votes.back().map(|vote| &vote.lockout)
626    }
627
628    pub fn last_voted_slot(&self) -> Option<Slot> {
629        self.last_lockout().map(|v| v.slot())
630    }
631
632    // Upto MAX_LOCKOUT_HISTORY many recent unexpired
633    // vote slots pushed onto the stack.
634    pub fn tower(&self) -> Vec<Slot> {
635        self.votes.iter().map(|v| v.slot()).collect()
636    }
637
638    pub fn current_epoch(&self) -> Epoch {
639        if self.epoch_credits.is_empty() {
640            0
641        } else {
642            self.epoch_credits.last().unwrap().0
643        }
644    }
645
646    /// Number of "credits" owed to this account from the mining pool. Submit this
647    /// VoteState to the Rewards program to trade credits for lamports.
648    pub fn credits(&self) -> u64 {
649        if self.epoch_credits.is_empty() {
650            0
651        } else {
652            self.epoch_credits.last().unwrap().1
653        }
654    }
655
656    /// Number of "credits" owed to this account from the mining pool on a per-epoch basis,
657    ///  starting from credits observed.
658    /// Each tuple of (Epoch, u64, u64) is read as (epoch, credits, prev_credits), where
659    ///   credits for each epoch is credits - prev_credits; while redundant this makes
660    ///   calculating rewards over partial epochs nice and simple
661    pub fn epoch_credits(&self) -> &Vec<(Epoch, u64, u64)> {
662        &self.epoch_credits
663    }
664
665    pub fn set_new_authorized_voter<F>(
666        &mut self,
667        authorized_pubkey: &Pubkey,
668        current_epoch: Epoch,
669        target_epoch: Epoch,
670        verify: F,
671    ) -> Result<(), InstructionError>
672    where
673        F: Fn(Pubkey) -> Result<(), InstructionError>,
674    {
675        let epoch_authorized_voter = self.get_and_update_authorized_voter(current_epoch)?;
676        verify(epoch_authorized_voter)?;
677
678        // The offset in slots `n` on which the target_epoch
679        // (default value `DEFAULT_LEADER_SCHEDULE_SLOT_OFFSET`) is
680        // calculated is the number of slots available from the
681        // first slot `S` of an epoch in which to set a new voter for
682        // the epoch at `S` + `n`
683        if self.authorized_voters.contains(target_epoch) {
684            return Err(VoteError::TooSoonToReauthorize.into());
685        }
686
687        // Get the latest authorized_voter
688        let (latest_epoch, latest_authorized_pubkey) = self
689            .authorized_voters
690            .last()
691            .ok_or(InstructionError::InvalidAccountData)?;
692
693        // If we're not setting the same pubkey as authorized pubkey again,
694        // then update the list of prior voters to mark the expiration
695        // of the old authorized pubkey
696        if latest_authorized_pubkey != authorized_pubkey {
697            // Update the epoch ranges of authorized pubkeys that will be expired
698            let epoch_of_last_authorized_switch =
699                self.prior_voters.last().map(|range| range.2).unwrap_or(0);
700
701            // target_epoch must:
702            // 1) Be monotonically increasing due to the clock always
703            //    moving forward
704            // 2) not be equal to latest epoch otherwise this
705            //    function would have returned TooSoonToReauthorize error
706            //    above
707            assert!(target_epoch > *latest_epoch);
708
709            // Commit the new state
710            self.prior_voters.append((
711                *latest_authorized_pubkey,
712                epoch_of_last_authorized_switch,
713                target_epoch,
714            ));
715        }
716
717        self.authorized_voters
718            .insert(target_epoch, *authorized_pubkey);
719
720        Ok(())
721    }
722
723    pub fn get_and_update_authorized_voter(
724        &mut self,
725        current_epoch: Epoch,
726    ) -> Result<Pubkey, InstructionError> {
727        let pubkey = self
728            .authorized_voters
729            .get_and_cache_authorized_voter_for_epoch(current_epoch)
730            .ok_or(InstructionError::InvalidAccountData)?;
731        self.authorized_voters
732            .purge_authorized_voters(current_epoch);
733        Ok(pubkey)
734    }
735
736    // Pop all recent votes that are not locked out at the next vote slot.  This
737    // allows validators to switch forks once their votes for another fork have
738    // expired. This also allows validators continue voting on recent blocks in
739    // the same fork without increasing lockouts.
740    pub fn pop_expired_votes(&mut self, next_vote_slot: Slot) {
741        while let Some(vote) = self.last_lockout() {
742            if !vote.is_locked_out_at_slot(next_vote_slot) {
743                self.votes.pop_back();
744            } else {
745                break;
746            }
747        }
748    }
749
750    pub fn double_lockouts(&mut self) {
751        let stack_depth = self.votes.len();
752        for (i, v) in self.votes.iter_mut().enumerate() {
753            // Don't increase the lockout for this vote until we get more confirmations
754            // than the max number of confirmations this vote has seen
755            if stack_depth >
756                i.checked_add(v.confirmation_count() as usize)
757                    .expect("`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`")
758            {
759                v.lockout.increase_confirmation_count(1);
760            }
761        }
762    }
763
764    pub fn process_timestamp(
765        &mut self,
766        slot: Slot,
767        timestamp: UnixTimestamp,
768    ) -> Result<(), VoteError> {
769        if (slot < self.last_timestamp.slot || timestamp < self.last_timestamp.timestamp)
770            || (slot == self.last_timestamp.slot
771                && BlockTimestamp { slot, timestamp } != self.last_timestamp
772                && self.last_timestamp.slot != 0)
773        {
774            return Err(VoteError::TimestampTooOld);
775        }
776        self.last_timestamp = BlockTimestamp { slot, timestamp };
777        Ok(())
778    }
779
780    pub fn is_correct_size_and_initialized(data: &[u8]) -> bool {
781        const VERSION_OFFSET: usize = 4;
782        const DEFAULT_PRIOR_VOTERS_END: usize = VERSION_OFFSET + DEFAULT_PRIOR_VOTERS_OFFSET;
783        data.len() == VoteState::size_of()
784            && data[VERSION_OFFSET..DEFAULT_PRIOR_VOTERS_END] != [0; DEFAULT_PRIOR_VOTERS_OFFSET]
785    }
786}
787
788pub mod serde_compact_vote_state_update {
789    use {
790        super::*,
791        crate::{
792            clock::{Slot, UnixTimestamp},
793            serde_varint, short_vec,
794            vote::state::Lockout,
795        },
796        serde::{Deserialize, Deserializer, Serialize, Serializer},
797    };
798
799    #[derive(Deserialize, Serialize, AbiExample)]
800    struct LockoutOffset {
801        #[serde(with = "serde_varint")]
802        offset: Slot,
803        confirmation_count: u8,
804    }
805
806    #[derive(Deserialize, Serialize)]
807    struct CompactVoteStateUpdate {
808        root: Slot,
809        #[serde(with = "short_vec")]
810        lockout_offsets: Vec<LockoutOffset>,
811        hash: Hash,
812        timestamp: Option<UnixTimestamp>,
813    }
814
815    pub fn serialize<S>(
816        vote_state_update: &VoteStateUpdate,
817        serializer: S,
818    ) -> Result<S::Ok, S::Error>
819    where
820        S: Serializer,
821    {
822        let lockout_offsets = vote_state_update.lockouts.iter().scan(
823            vote_state_update.root.unwrap_or_default(),
824            |slot, lockout| {
825                let Some(offset) = lockout.slot().checked_sub(*slot) else {
826                    return Some(Err(serde::ser::Error::custom("Invalid vote lockout")));
827                };
828                let Ok(confirmation_count) = u8::try_from(lockout.confirmation_count()) else {
829                    return Some(Err(serde::ser::Error::custom("Invalid confirmation count")));
830                };
831                let lockout_offset = LockoutOffset {
832                    offset,
833                    confirmation_count,
834                };
835                *slot = lockout.slot();
836                Some(Ok(lockout_offset))
837            },
838        );
839        let compact_vote_state_update = CompactVoteStateUpdate {
840            root: vote_state_update.root.unwrap_or(Slot::MAX),
841            lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
842            hash: vote_state_update.hash,
843            timestamp: vote_state_update.timestamp,
844        };
845        compact_vote_state_update.serialize(serializer)
846    }
847
848    pub fn deserialize<'de, D>(deserializer: D) -> Result<VoteStateUpdate, D::Error>
849    where
850        D: Deserializer<'de>,
851    {
852        let CompactVoteStateUpdate {
853            root,
854            lockout_offsets,
855            hash,
856            timestamp,
857        } = CompactVoteStateUpdate::deserialize(deserializer)?;
858        let root = (root != Slot::MAX).then_some(root);
859        let lockouts =
860            lockout_offsets
861                .iter()
862                .scan(root.unwrap_or_default(), |slot, lockout_offset| {
863                    *slot = match slot.checked_add(lockout_offset.offset) {
864                        None => {
865                            return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
866                        }
867                        Some(slot) => slot,
868                    };
869                    let lockout = Lockout::new_with_confirmation_count(
870                        *slot,
871                        u32::from(lockout_offset.confirmation_count),
872                    );
873                    Some(Ok(lockout))
874                });
875        Ok(VoteStateUpdate {
876            root,
877            lockouts: lockouts.collect::<Result<_, _>>()?,
878            hash,
879            timestamp,
880        })
881    }
882}
883
884#[cfg(test)]
885mod tests {
886    use {super::*, itertools::Itertools, rand::Rng};
887
888    #[test]
889    fn test_vote_serialize() {
890        let mut buffer: Vec<u8> = vec![0; VoteState::size_of()];
891        let mut vote_state = VoteState::default();
892        vote_state
893            .votes
894            .resize(MAX_LOCKOUT_HISTORY, LandedVote::default());
895        vote_state.root_slot = Some(1);
896        let versioned = VoteStateVersions::new_current(vote_state);
897        assert!(VoteState::serialize(&versioned, &mut buffer[0..4]).is_err());
898        VoteState::serialize(&versioned, &mut buffer).unwrap();
899        assert_eq!(
900            VoteState::deserialize(&buffer).unwrap(),
901            versioned.convert_to_current()
902        );
903    }
904
905    #[test]
906    fn test_vote_deserialize_into() {
907        // base case
908        let target_vote_state = VoteState::default();
909        let vote_state_buf =
910            bincode::serialize(&VoteStateVersions::new_current(target_vote_state.clone())).unwrap();
911
912        let mut test_vote_state = VoteState::default();
913        VoteState::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
914
915        assert_eq!(target_vote_state, test_vote_state);
916
917        // variant
918        // provide 4x the minimum struct size in bytes to ensure we typically touch every field
919        let struct_bytes_x4 = std::mem::size_of::<VoteState>() * 4;
920        for _ in 0..1000 {
921            let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
922            let mut unstructured = Unstructured::new(&raw_data);
923
924            let target_vote_state_versions =
925                VoteStateVersions::arbitrary(&mut unstructured).unwrap();
926            let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
927            let target_vote_state = target_vote_state_versions.convert_to_current();
928
929            let mut test_vote_state = VoteState::default();
930            VoteState::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
931
932            assert_eq!(target_vote_state, test_vote_state);
933        }
934    }
935
936    #[test]
937    fn test_vote_deserialize_into_nopanic() {
938        // base case
939        let mut test_vote_state = VoteState::default();
940        let e = VoteState::deserialize_into(&[], &mut test_vote_state).unwrap_err();
941        assert_eq!(e, InstructionError::InvalidAccountData);
942
943        // variant
944        let serialized_len_x4 = serialized_size(&test_vote_state).unwrap() * 4;
945        let mut rng = rand::thread_rng();
946        for _ in 0..1000 {
947            let raw_data_length = rng.gen_range(1..serialized_len_x4);
948            let raw_data: Vec<u8> = (0..raw_data_length).map(|_| rng.gen::<u8>()).collect();
949
950            // it is extremely improbable, though theoretically possible, for random bytes to be syntactically valid
951            // so we only check that the deserialize function does not panic
952            let mut test_vote_state = VoteState::default();
953            let _ = VoteState::deserialize_into(&raw_data, &mut test_vote_state);
954        }
955    }
956
957    #[test]
958    fn test_vote_state_commission_split() {
959        let vote_state = VoteState::default();
960
961        assert_eq!(vote_state.commission_split(1), (0, 1, false));
962
963        let mut vote_state = VoteState {
964            commission: std::u8::MAX,
965            ..VoteState::default()
966        };
967        assert_eq!(vote_state.commission_split(1), (1, 0, false));
968
969        vote_state.commission = 99;
970        assert_eq!(vote_state.commission_split(10), (9, 0, true));
971
972        vote_state.commission = 1;
973        assert_eq!(vote_state.commission_split(10), (0, 9, true));
974
975        vote_state.commission = 50;
976        let (voter_portion, staker_portion, was_split) = vote_state.commission_split(10);
977
978        assert_eq!((voter_portion, staker_portion, was_split), (5, 5, true));
979    }
980
981    #[test]
982    fn test_vote_state_epoch_credits() {
983        let mut vote_state = VoteState::default();
984
985        assert_eq!(vote_state.credits(), 0);
986        assert_eq!(vote_state.epoch_credits().clone(), vec![]);
987
988        let mut expected = vec![];
989        let mut credits = 0;
990        let epochs = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
991        for epoch in 0..epochs {
992            for _j in 0..epoch {
993                vote_state.increment_credits(epoch, 1);
994                credits += 1;
995            }
996            expected.push((epoch, credits, credits - epoch));
997        }
998
999        while expected.len() > MAX_EPOCH_CREDITS_HISTORY {
1000            expected.remove(0);
1001        }
1002
1003        assert_eq!(vote_state.credits(), credits);
1004        assert_eq!(vote_state.epoch_credits().clone(), expected);
1005    }
1006
1007    #[test]
1008    fn test_vote_state_epoch0_no_credits() {
1009        let mut vote_state = VoteState::default();
1010
1011        assert_eq!(vote_state.epoch_credits().len(), 0);
1012        vote_state.increment_credits(1, 1);
1013        assert_eq!(vote_state.epoch_credits().len(), 1);
1014
1015        vote_state.increment_credits(2, 1);
1016        assert_eq!(vote_state.epoch_credits().len(), 2);
1017    }
1018
1019    #[test]
1020    fn test_vote_state_increment_credits() {
1021        let mut vote_state = VoteState::default();
1022
1023        let credits = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
1024        for i in 0..credits {
1025            vote_state.increment_credits(i, 1);
1026        }
1027        assert_eq!(vote_state.credits(), credits);
1028        assert!(vote_state.epoch_credits().len() <= MAX_EPOCH_CREDITS_HISTORY);
1029    }
1030
1031    #[test]
1032    fn test_vote_process_timestamp() {
1033        let (slot, timestamp) = (15, 1_575_412_285);
1034        let mut vote_state = VoteState {
1035            last_timestamp: BlockTimestamp { slot, timestamp },
1036            ..VoteState::default()
1037        };
1038
1039        assert_eq!(
1040            vote_state.process_timestamp(slot - 1, timestamp + 1),
1041            Err(VoteError::TimestampTooOld)
1042        );
1043        assert_eq!(
1044            vote_state.last_timestamp,
1045            BlockTimestamp { slot, timestamp }
1046        );
1047        assert_eq!(
1048            vote_state.process_timestamp(slot + 1, timestamp - 1),
1049            Err(VoteError::TimestampTooOld)
1050        );
1051        assert_eq!(
1052            vote_state.process_timestamp(slot, timestamp + 1),
1053            Err(VoteError::TimestampTooOld)
1054        );
1055        assert_eq!(vote_state.process_timestamp(slot, timestamp), Ok(()));
1056        assert_eq!(
1057            vote_state.last_timestamp,
1058            BlockTimestamp { slot, timestamp }
1059        );
1060        assert_eq!(vote_state.process_timestamp(slot + 1, timestamp), Ok(()));
1061        assert_eq!(
1062            vote_state.last_timestamp,
1063            BlockTimestamp {
1064                slot: slot + 1,
1065                timestamp
1066            }
1067        );
1068        assert_eq!(
1069            vote_state.process_timestamp(slot + 2, timestamp + 1),
1070            Ok(())
1071        );
1072        assert_eq!(
1073            vote_state.last_timestamp,
1074            BlockTimestamp {
1075                slot: slot + 2,
1076                timestamp: timestamp + 1
1077            }
1078        );
1079
1080        // Test initial vote
1081        vote_state.last_timestamp = BlockTimestamp::default();
1082        assert_eq!(vote_state.process_timestamp(0, timestamp), Ok(()));
1083    }
1084
1085    #[test]
1086    fn test_get_and_update_authorized_voter() {
1087        let original_voter = Pubkey::new_unique();
1088        let mut vote_state = VoteState::new(
1089            &VoteInit {
1090                node_pubkey: original_voter,
1091                authorized_voter: original_voter,
1092                authorized_withdrawer: original_voter,
1093                commission: 0,
1094            },
1095            &Clock::default(),
1096        );
1097
1098        assert_eq!(vote_state.authorized_voters.len(), 1);
1099        assert_eq!(
1100            *vote_state.authorized_voters.first().unwrap().1,
1101            original_voter
1102        );
1103
1104        // If no new authorized voter was set, the same authorized voter
1105        // is locked into the next epoch
1106        assert_eq!(
1107            vote_state.get_and_update_authorized_voter(1).unwrap(),
1108            original_voter
1109        );
1110
1111        // Try to get the authorized voter for epoch 5, implies
1112        // the authorized voter for epochs 1-4 were unchanged
1113        assert_eq!(
1114            vote_state.get_and_update_authorized_voter(5).unwrap(),
1115            original_voter
1116        );
1117
1118        // Authorized voter for expired epoch 0..5 should have been
1119        // purged and no longer queryable
1120        assert_eq!(vote_state.authorized_voters.len(), 1);
1121        for i in 0..5 {
1122            assert!(vote_state
1123                .authorized_voters
1124                .get_authorized_voter(i)
1125                .is_none());
1126        }
1127
1128        // Set an authorized voter change at slot 7
1129        let new_authorized_voter = Pubkey::new_unique();
1130        vote_state
1131            .set_new_authorized_voter(&new_authorized_voter, 5, 7, |_| Ok(()))
1132            .unwrap();
1133
1134        // Try to get the authorized voter for epoch 6, unchanged
1135        assert_eq!(
1136            vote_state.get_and_update_authorized_voter(6).unwrap(),
1137            original_voter
1138        );
1139
1140        // Try to get the authorized voter for epoch 7 and onwards, should
1141        // be the new authorized voter
1142        for i in 7..10 {
1143            assert_eq!(
1144                vote_state.get_and_update_authorized_voter(i).unwrap(),
1145                new_authorized_voter
1146            );
1147        }
1148        assert_eq!(vote_state.authorized_voters.len(), 1);
1149    }
1150
1151    #[test]
1152    fn test_set_new_authorized_voter() {
1153        let original_voter = Pubkey::new_unique();
1154        let epoch_offset = 15;
1155        let mut vote_state = VoteState::new(
1156            &VoteInit {
1157                node_pubkey: original_voter,
1158                authorized_voter: original_voter,
1159                authorized_withdrawer: original_voter,
1160                commission: 0,
1161            },
1162            &Clock::default(),
1163        );
1164
1165        assert!(vote_state.prior_voters.last().is_none());
1166
1167        let new_voter = Pubkey::new_unique();
1168        // Set a new authorized voter
1169        vote_state
1170            .set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(()))
1171            .unwrap();
1172
1173        assert_eq!(vote_state.prior_voters.idx, 0);
1174        assert_eq!(
1175            vote_state.prior_voters.last(),
1176            Some(&(original_voter, 0, epoch_offset))
1177        );
1178
1179        // Trying to set authorized voter for same epoch again should fail
1180        assert_eq!(
1181            vote_state.set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(())),
1182            Err(VoteError::TooSoonToReauthorize.into())
1183        );
1184
1185        // Setting the same authorized voter again should succeed
1186        vote_state
1187            .set_new_authorized_voter(&new_voter, 2, 2 + epoch_offset, |_| Ok(()))
1188            .unwrap();
1189
1190        // Set a third and fourth authorized voter
1191        let new_voter2 = Pubkey::new_unique();
1192        vote_state
1193            .set_new_authorized_voter(&new_voter2, 3, 3 + epoch_offset, |_| Ok(()))
1194            .unwrap();
1195        assert_eq!(vote_state.prior_voters.idx, 1);
1196        assert_eq!(
1197            vote_state.prior_voters.last(),
1198            Some(&(new_voter, epoch_offset, 3 + epoch_offset))
1199        );
1200
1201        let new_voter3 = Pubkey::new_unique();
1202        vote_state
1203            .set_new_authorized_voter(&new_voter3, 6, 6 + epoch_offset, |_| Ok(()))
1204            .unwrap();
1205        assert_eq!(vote_state.prior_voters.idx, 2);
1206        assert_eq!(
1207            vote_state.prior_voters.last(),
1208            Some(&(new_voter2, 3 + epoch_offset, 6 + epoch_offset))
1209        );
1210
1211        // Check can set back to original voter
1212        vote_state
1213            .set_new_authorized_voter(&original_voter, 9, 9 + epoch_offset, |_| Ok(()))
1214            .unwrap();
1215
1216        // Run with these voters for a while, check the ranges of authorized
1217        // voters is correct
1218        for i in 9..epoch_offset {
1219            assert_eq!(
1220                vote_state.get_and_update_authorized_voter(i).unwrap(),
1221                original_voter
1222            );
1223        }
1224        for i in epoch_offset..3 + epoch_offset {
1225            assert_eq!(
1226                vote_state.get_and_update_authorized_voter(i).unwrap(),
1227                new_voter
1228            );
1229        }
1230        for i in 3 + epoch_offset..6 + epoch_offset {
1231            assert_eq!(
1232                vote_state.get_and_update_authorized_voter(i).unwrap(),
1233                new_voter2
1234            );
1235        }
1236        for i in 6 + epoch_offset..9 + epoch_offset {
1237            assert_eq!(
1238                vote_state.get_and_update_authorized_voter(i).unwrap(),
1239                new_voter3
1240            );
1241        }
1242        for i in 9 + epoch_offset..=10 + epoch_offset {
1243            assert_eq!(
1244                vote_state.get_and_update_authorized_voter(i).unwrap(),
1245                original_voter
1246            );
1247        }
1248    }
1249
1250    #[test]
1251    fn test_authorized_voter_is_locked_within_epoch() {
1252        let original_voter = Pubkey::new_unique();
1253        let mut vote_state = VoteState::new(
1254            &VoteInit {
1255                node_pubkey: original_voter,
1256                authorized_voter: original_voter,
1257                authorized_withdrawer: original_voter,
1258                commission: 0,
1259            },
1260            &Clock::default(),
1261        );
1262
1263        // Test that it's not possible to set a new authorized
1264        // voter within the same epoch, even if none has been
1265        // explicitly set before
1266        let new_voter = Pubkey::new_unique();
1267        assert_eq!(
1268            vote_state.set_new_authorized_voter(&new_voter, 1, 1, |_| Ok(())),
1269            Err(VoteError::TooSoonToReauthorize.into())
1270        );
1271
1272        assert_eq!(vote_state.get_authorized_voter(1), Some(original_voter));
1273
1274        // Set a new authorized voter for a future epoch
1275        assert_eq!(
1276            vote_state.set_new_authorized_voter(&new_voter, 1, 2, |_| Ok(())),
1277            Ok(())
1278        );
1279
1280        // Test that it's not possible to set a new authorized
1281        // voter within the same epoch, even if none has been
1282        // explicitly set before
1283        assert_eq!(
1284            vote_state.set_new_authorized_voter(&original_voter, 3, 3, |_| Ok(())),
1285            Err(VoteError::TooSoonToReauthorize.into())
1286        );
1287
1288        assert_eq!(vote_state.get_authorized_voter(3), Some(new_voter));
1289    }
1290
1291    #[test]
1292    fn test_vote_state_size_of() {
1293        let vote_state = VoteState::get_max_sized_vote_state();
1294        let vote_state = VoteStateVersions::new_current(vote_state);
1295        let size = serialized_size(&vote_state).unwrap();
1296        assert_eq!(VoteState::size_of() as u64, size);
1297    }
1298
1299    #[test]
1300    fn test_vote_state_max_size() {
1301        let mut max_sized_data = vec![0; VoteState::size_of()];
1302        let vote_state = VoteState::get_max_sized_vote_state();
1303        let (start_leader_schedule_epoch, _) = vote_state.authorized_voters.last().unwrap();
1304        let start_current_epoch =
1305            start_leader_schedule_epoch - MAX_LEADER_SCHEDULE_EPOCH_OFFSET + 1;
1306
1307        let mut vote_state = Some(vote_state);
1308        for i in start_current_epoch..start_current_epoch + 2 * MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
1309            vote_state.as_mut().map(|vote_state| {
1310                vote_state.set_new_authorized_voter(
1311                    &Pubkey::new_unique(),
1312                    i,
1313                    i + MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
1314                    |_| Ok(()),
1315                )
1316            });
1317
1318            let versioned = VoteStateVersions::new_current(vote_state.take().unwrap());
1319            VoteState::serialize(&versioned, &mut max_sized_data).unwrap();
1320            vote_state = Some(versioned.convert_to_current());
1321        }
1322    }
1323
1324    #[test]
1325    fn test_default_vote_state_is_uninitialized() {
1326        // The default `VoteState` is stored to de-initialize a zero-balance vote account,
1327        // so must remain such that `VoteStateVersions::is_uninitialized()` returns true
1328        // when called on a `VoteStateVersions` that stores it
1329        assert!(VoteStateVersions::new_current(VoteState::default()).is_uninitialized());
1330    }
1331
1332    #[test]
1333    fn test_is_correct_size_and_initialized() {
1334        // Check all zeroes
1335        let mut vote_account_data = vec![0; VoteStateVersions::vote_state_size_of(true)];
1336        assert!(!VoteStateVersions::is_correct_size_and_initialized(
1337            &vote_account_data
1338        ));
1339
1340        // Check default VoteState
1341        let default_account_state = VoteStateVersions::new_current(VoteState::default());
1342        VoteState::serialize(&default_account_state, &mut vote_account_data).unwrap();
1343        assert!(!VoteStateVersions::is_correct_size_and_initialized(
1344            &vote_account_data
1345        ));
1346
1347        // Check non-zero data shorter than offset index used
1348        let short_data = vec![1; DEFAULT_PRIOR_VOTERS_OFFSET];
1349        assert!(!VoteStateVersions::is_correct_size_and_initialized(
1350            &short_data
1351        ));
1352
1353        // Check non-zero large account
1354        let mut large_vote_data = vec![1; 2 * VoteStateVersions::vote_state_size_of(true)];
1355        let default_account_state = VoteStateVersions::new_current(VoteState::default());
1356        VoteState::serialize(&default_account_state, &mut large_vote_data).unwrap();
1357        assert!(!VoteStateVersions::is_correct_size_and_initialized(
1358            &vote_account_data
1359        ));
1360
1361        // Check populated VoteState
1362        let vote_state = VoteState::new(
1363            &VoteInit {
1364                node_pubkey: Pubkey::new_unique(),
1365                authorized_voter: Pubkey::new_unique(),
1366                authorized_withdrawer: Pubkey::new_unique(),
1367                commission: 0,
1368            },
1369            &Clock::default(),
1370        );
1371        let account_state = VoteStateVersions::new_current(vote_state.clone());
1372        VoteState::serialize(&account_state, &mut vote_account_data).unwrap();
1373        assert!(VoteStateVersions::is_correct_size_and_initialized(
1374            &vote_account_data
1375        ));
1376
1377        // Check old VoteState that hasn't been upgraded to newest version yet
1378        let old_vote_state = VoteState1_14_11::from(vote_state);
1379        let account_state = VoteStateVersions::V1_14_11(Box::new(old_vote_state));
1380        let mut vote_account_data = vec![0; VoteStateVersions::vote_state_size_of(false)];
1381        VoteState::serialize(&account_state, &mut vote_account_data).unwrap();
1382        assert!(VoteStateVersions::is_correct_size_and_initialized(
1383            &vote_account_data
1384        ));
1385    }
1386
1387    #[test]
1388    fn test_minimum_balance() {
1389        let rent = miraland_program::rent::Rent::default();
1390        let minimum_balance = rent.minimum_balance(VoteState::size_of());
1391        // golden, may need updating when vote_state grows
1392        assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)
1393    }
1394
1395    #[test]
1396    fn test_serde_compact_vote_state_update() {
1397        let mut rng = rand::thread_rng();
1398        for _ in 0..5000 {
1399            run_serde_compact_vote_state_update(&mut rng);
1400        }
1401    }
1402
1403    fn run_serde_compact_vote_state_update<R: Rng>(rng: &mut R) {
1404        let lockouts: VecDeque<_> = std::iter::repeat_with(|| {
1405            let slot = 149_303_885_u64.saturating_add(rng.gen_range(0..10_000));
1406            let confirmation_count = rng.gen_range(0..33);
1407            Lockout::new_with_confirmation_count(slot, confirmation_count)
1408        })
1409        .take(32)
1410        .sorted_by_key(|lockout| lockout.slot())
1411        .collect();
1412        let root = rng.gen_ratio(1, 2).then(|| {
1413            lockouts[0]
1414                .slot()
1415                .checked_sub(rng.gen_range(0..1_000))
1416                .expect("All slots should be greater than 1_000")
1417        });
1418        let timestamp = rng.gen_ratio(1, 2).then(|| rng.gen());
1419        let hash = Hash::from(rng.gen::<[u8; 32]>());
1420        let vote_state_update = VoteStateUpdate {
1421            lockouts,
1422            root,
1423            hash,
1424            timestamp,
1425        };
1426        #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
1427        enum VoteInstruction {
1428            #[serde(with = "serde_compact_vote_state_update")]
1429            UpdateVoteState(VoteStateUpdate),
1430            UpdateVoteStateSwitch(
1431                #[serde(with = "serde_compact_vote_state_update")] VoteStateUpdate,
1432                Hash,
1433            ),
1434        }
1435        let vote = VoteInstruction::UpdateVoteState(vote_state_update.clone());
1436        let bytes = bincode::serialize(&vote).unwrap();
1437        assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1438        let hash = Hash::from(rng.gen::<[u8; 32]>());
1439        let vote = VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash);
1440        let bytes = bincode::serialize(&vote).unwrap();
1441        assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1442    }
1443}