solana_vote_interface/state/
mod.rs

1//! Vote state
2
3#[cfg(feature = "dev-context-only-utils")]
4use arbitrary::Arbitrary;
5#[cfg(all(not(target_os = "solana"), feature = "bincode"))]
6use bincode::deserialize;
7#[cfg(feature = "bincode")]
8use bincode::{serialize_into, ErrorKind};
9#[cfg(feature = "serde")]
10use serde_derive::{Deserialize, Serialize};
11#[cfg(feature = "frozen-abi")]
12use solana_frozen_abi_macro::{frozen_abi, AbiExample};
13use {
14    crate::{authorized_voters::AuthorizedVoters, error::VoteError},
15    solana_clock::{Clock, Epoch, Slot, UnixTimestamp},
16    solana_hash::Hash,
17    solana_instruction::error::InstructionError,
18    solana_pubkey::Pubkey,
19    solana_rent::Rent,
20    std::{collections::VecDeque, fmt::Debug},
21};
22#[cfg(test)]
23use {arbitrary::Unstructured, solana_epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET};
24
25mod vote_state_0_23_5;
26pub mod vote_state_1_14_11;
27pub use vote_state_1_14_11::*;
28#[cfg(any(target_os = "solana", feature = "bincode"))]
29mod vote_state_deserialize;
30#[cfg(any(target_os = "solana", feature = "bincode"))]
31use vote_state_deserialize::deserialize_vote_state_into;
32pub mod vote_state_versions;
33pub use vote_state_versions::*;
34
35// Maximum number of votes to keep around, tightly coupled with epoch_schedule::MINIMUM_SLOTS_PER_EPOCH
36pub const MAX_LOCKOUT_HISTORY: usize = 31;
37pub const INITIAL_LOCKOUT: usize = 2;
38
39// Maximum number of credits history to keep around
40pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
41
42// Offset of VoteState::prior_voters, for determining initialization status without deserialization
43const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 114;
44
45// 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.
46pub const VOTE_CREDITS_GRACE_SLOTS: u8 = 2;
47
48// 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.
49pub const VOTE_CREDITS_MAXIMUM_PER_SLOT: u8 = 16;
50
51#[cfg_attr(
52    feature = "frozen-abi",
53    frozen_abi(digest = "GvUzgtcxhKVVxPAjSntXGPqjLZK5ovgZzCiUP1tDpB9q"),
54    derive(AbiExample)
55)]
56#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
57#[derive(Default, Debug, PartialEq, Eq, Clone)]
58pub struct Vote {
59    /// A stack of votes starting with the oldest vote
60    pub slots: Vec<Slot>,
61    /// signature of the bank's state at the last slot
62    pub hash: Hash,
63    /// processing timestamp of last slot
64    pub timestamp: Option<UnixTimestamp>,
65}
66
67impl Vote {
68    pub fn new(slots: Vec<Slot>, hash: Hash) -> Self {
69        Self {
70            slots,
71            hash,
72            timestamp: None,
73        }
74    }
75
76    pub fn last_voted_slot(&self) -> Option<Slot> {
77        self.slots.last().copied()
78    }
79}
80
81#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
82#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
83#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
84#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
85pub struct Lockout {
86    slot: Slot,
87    confirmation_count: u32,
88}
89
90impl Lockout {
91    pub fn new(slot: Slot) -> Self {
92        Self::new_with_confirmation_count(slot, 1)
93    }
94
95    pub fn new_with_confirmation_count(slot: Slot, confirmation_count: u32) -> Self {
96        Self {
97            slot,
98            confirmation_count,
99        }
100    }
101
102    // The number of slots for which this vote is locked
103    pub fn lockout(&self) -> u64 {
104        (INITIAL_LOCKOUT as u64).wrapping_pow(std::cmp::min(
105            self.confirmation_count(),
106            MAX_LOCKOUT_HISTORY as u32,
107        ))
108    }
109
110    // The last slot at which a vote is still locked out. Validators should not
111    // vote on a slot in another fork which is less than or equal to this slot
112    // to avoid having their stake slashed.
113    pub fn last_locked_out_slot(&self) -> Slot {
114        self.slot.saturating_add(self.lockout())
115    }
116
117    pub fn is_locked_out_at_slot(&self, slot: Slot) -> bool {
118        self.last_locked_out_slot() >= slot
119    }
120
121    pub fn slot(&self) -> Slot {
122        self.slot
123    }
124
125    pub fn confirmation_count(&self) -> u32 {
126        self.confirmation_count
127    }
128
129    pub fn increase_confirmation_count(&mut self, by: u32) {
130        self.confirmation_count = self.confirmation_count.saturating_add(by)
131    }
132}
133
134#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
135#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
136#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
137#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
138pub struct LandedVote {
139    // Latency is the difference in slot number between the slot that was voted on (lockout.slot) and the slot in
140    // which the vote that added this Lockout landed.  For votes which were cast before versions of the validator
141    // software which recorded vote latencies, latency is recorded as 0.
142    pub latency: u8,
143    pub lockout: Lockout,
144}
145
146impl LandedVote {
147    pub fn slot(&self) -> Slot {
148        self.lockout.slot
149    }
150
151    pub fn confirmation_count(&self) -> u32 {
152        self.lockout.confirmation_count
153    }
154}
155
156impl From<LandedVote> for Lockout {
157    fn from(landed_vote: LandedVote) -> Self {
158        landed_vote.lockout
159    }
160}
161
162impl From<Lockout> for LandedVote {
163    fn from(lockout: Lockout) -> Self {
164        Self {
165            latency: 0,
166            lockout,
167        }
168    }
169}
170
171#[cfg_attr(
172    feature = "frozen-abi",
173    frozen_abi(digest = "CxyuwbaEdzP7jDCZyxjgQvLGXadBUZF3LoUvbSpQ6tYN"),
174    derive(AbiExample)
175)]
176#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
177#[derive(Default, Debug, PartialEq, Eq, Clone)]
178pub struct VoteStateUpdate {
179    /// The proposed tower
180    pub lockouts: VecDeque<Lockout>,
181    /// The proposed root
182    pub root: Option<Slot>,
183    /// signature of the bank's state at the last slot
184    pub hash: Hash,
185    /// processing timestamp of last slot
186    pub timestamp: Option<UnixTimestamp>,
187}
188
189impl From<Vec<(Slot, u32)>> for VoteStateUpdate {
190    fn from(recent_slots: Vec<(Slot, u32)>) -> Self {
191        let lockouts: VecDeque<Lockout> = recent_slots
192            .into_iter()
193            .map(|(slot, confirmation_count)| {
194                Lockout::new_with_confirmation_count(slot, confirmation_count)
195            })
196            .collect();
197        Self {
198            lockouts,
199            root: None,
200            hash: Hash::default(),
201            timestamp: None,
202        }
203    }
204}
205
206impl VoteStateUpdate {
207    pub fn new(lockouts: VecDeque<Lockout>, root: Option<Slot>, hash: Hash) -> Self {
208        Self {
209            lockouts,
210            root,
211            hash,
212            timestamp: None,
213        }
214    }
215
216    pub fn slots(&self) -> Vec<Slot> {
217        self.lockouts.iter().map(|lockout| lockout.slot()).collect()
218    }
219
220    pub fn last_voted_slot(&self) -> Option<Slot> {
221        self.lockouts.back().map(|l| l.slot())
222    }
223}
224
225#[cfg_attr(
226    feature = "frozen-abi",
227    frozen_abi(digest = "6UDiQMH4wbNwkMHosPMtekMYu2Qa6CHPZ2ymK4mc6FGu"),
228    derive(AbiExample)
229)]
230#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
231#[derive(Default, Debug, PartialEq, Eq, Clone)]
232pub struct TowerSync {
233    /// The proposed tower
234    pub lockouts: VecDeque<Lockout>,
235    /// The proposed root
236    pub root: Option<Slot>,
237    /// signature of the bank's state at the last slot
238    pub hash: Hash,
239    /// processing timestamp of last slot
240    pub timestamp: Option<UnixTimestamp>,
241    /// the unique identifier for the chain up to and
242    /// including this block. Does not require replaying
243    /// in order to compute.
244    pub block_id: Hash,
245}
246
247impl From<Vec<(Slot, u32)>> for TowerSync {
248    fn from(recent_slots: Vec<(Slot, u32)>) -> Self {
249        let lockouts: VecDeque<Lockout> = recent_slots
250            .into_iter()
251            .map(|(slot, confirmation_count)| {
252                Lockout::new_with_confirmation_count(slot, confirmation_count)
253            })
254            .collect();
255        Self {
256            lockouts,
257            root: None,
258            hash: Hash::default(),
259            timestamp: None,
260            block_id: Hash::default(),
261        }
262    }
263}
264
265impl TowerSync {
266    pub fn new(
267        lockouts: VecDeque<Lockout>,
268        root: Option<Slot>,
269        hash: Hash,
270        block_id: Hash,
271    ) -> Self {
272        Self {
273            lockouts,
274            root,
275            hash,
276            timestamp: None,
277            block_id,
278        }
279    }
280
281    /// Creates a tower with consecutive votes for `slot - MAX_LOCKOUT_HISTORY + 1` to `slot` inclusive.
282    /// If `slot >= MAX_LOCKOUT_HISTORY`, sets the root to `(slot - MAX_LOCKOUT_HISTORY)`
283    /// Sets the hash to `hash` and leaves `block_id` unset.
284    pub fn new_from_slot(slot: Slot, hash: Hash) -> Self {
285        let lowest_slot = slot
286            .saturating_add(1)
287            .saturating_sub(MAX_LOCKOUT_HISTORY as u64);
288        let slots: Vec<_> = (lowest_slot..slot.saturating_add(1)).collect();
289        Self::new_from_slots(
290            slots,
291            hash,
292            (lowest_slot > 0).then(|| lowest_slot.saturating_sub(1)),
293        )
294    }
295
296    /// Creates a tower with consecutive confirmation for `slots`
297    pub fn new_from_slots(slots: Vec<Slot>, hash: Hash, root: Option<Slot>) -> Self {
298        let lockouts: VecDeque<Lockout> = slots
299            .into_iter()
300            .rev()
301            .enumerate()
302            .map(|(cc, s)| Lockout::new_with_confirmation_count(s, cc.saturating_add(1) as u32))
303            .rev()
304            .collect();
305        Self {
306            lockouts,
307            hash,
308            root,
309            timestamp: None,
310            block_id: Hash::default(),
311        }
312    }
313
314    pub fn slots(&self) -> Vec<Slot> {
315        self.lockouts.iter().map(|lockout| lockout.slot()).collect()
316    }
317
318    pub fn last_voted_slot(&self) -> Option<Slot> {
319        self.lockouts.back().map(|l| l.slot())
320    }
321}
322
323#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
324#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
325pub struct VoteInit {
326    pub node_pubkey: Pubkey,
327    pub authorized_voter: Pubkey,
328    pub authorized_withdrawer: Pubkey,
329    pub commission: u8,
330}
331
332#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
333#[derive(Debug, PartialEq, Eq, Clone, Copy)]
334pub enum VoteAuthorize {
335    Voter,
336    Withdrawer,
337}
338
339#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
340#[derive(Debug, PartialEq, Eq, Clone)]
341pub struct VoteAuthorizeWithSeedArgs {
342    pub authorization_type: VoteAuthorize,
343    pub current_authority_derived_key_owner: Pubkey,
344    pub current_authority_derived_key_seed: String,
345    pub new_authority: Pubkey,
346}
347
348#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
349#[derive(Debug, PartialEq, Eq, Clone)]
350pub struct VoteAuthorizeCheckedWithSeedArgs {
351    pub authorization_type: VoteAuthorize,
352    pub current_authority_derived_key_owner: Pubkey,
353    pub current_authority_derived_key_seed: String,
354}
355
356#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
357#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
358#[derive(Debug, Default, PartialEq, Eq, Clone)]
359#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
360pub struct BlockTimestamp {
361    pub slot: Slot,
362    pub timestamp: UnixTimestamp,
363}
364
365// this is how many epochs a voter can be remembered for slashing
366const MAX_ITEMS: usize = 32;
367
368#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
369#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
370#[derive(Debug, PartialEq, Eq, Clone)]
371#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
372pub struct CircBuf<I> {
373    buf: [I; MAX_ITEMS],
374    /// next pointer
375    idx: usize,
376    is_empty: bool,
377}
378
379impl<I: Default + Copy> Default for CircBuf<I> {
380    fn default() -> Self {
381        Self {
382            buf: [I::default(); MAX_ITEMS],
383            idx: MAX_ITEMS
384                .checked_sub(1)
385                .expect("`MAX_ITEMS` should be positive"),
386            is_empty: true,
387        }
388    }
389}
390
391impl<I> CircBuf<I> {
392    pub fn append(&mut self, item: I) {
393        // remember prior delegate and when we switched, to support later slashing
394        self.idx = self
395            .idx
396            .checked_add(1)
397            .and_then(|idx| idx.checked_rem(MAX_ITEMS))
398            .expect("`self.idx` should be < `MAX_ITEMS` which should be non-zero");
399
400        self.buf[self.idx] = item;
401        self.is_empty = false;
402    }
403
404    pub fn buf(&self) -> &[I; MAX_ITEMS] {
405        &self.buf
406    }
407
408    pub fn last(&self) -> Option<&I> {
409        if !self.is_empty {
410            self.buf.get(self.idx)
411        } else {
412            None
413        }
414    }
415}
416
417#[cfg_attr(
418    feature = "frozen-abi",
419    frozen_abi(digest = "BRwozbypfYXsHqFVj9w3iH5x1ak2NWHqCCn6pr3gHBkG"),
420    derive(AbiExample)
421)]
422#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
423#[derive(Debug, Default, PartialEq, Eq, Clone)]
424#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
425pub struct VoteState {
426    /// the node that votes in this account
427    pub node_pubkey: Pubkey,
428
429    /// the signer for withdrawals
430    pub authorized_withdrawer: Pubkey,
431    /// percentage (0-100) that represents what part of a rewards
432    ///  payout should be given to this VoteAccount
433    pub commission: u8,
434
435    pub votes: VecDeque<LandedVote>,
436
437    // This usually the last Lockout which was popped from self.votes.
438    // However, it can be arbitrary slot, when being used inside Tower
439    pub root_slot: Option<Slot>,
440
441    /// the signer for vote transactions
442    authorized_voters: AuthorizedVoters,
443
444    /// history of prior authorized voters and the epochs for which
445    /// they were set, the bottom end of the range is inclusive,
446    /// the top of the range is exclusive
447    prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>,
448
449    /// history of how many credits earned by the end of each epoch
450    ///  each tuple is (Epoch, credits, prev_credits)
451    pub epoch_credits: Vec<(Epoch, u64, u64)>,
452
453    /// most recent timestamp submitted with a vote
454    pub last_timestamp: BlockTimestamp,
455}
456
457impl VoteState {
458    pub fn new(vote_init: &VoteInit, clock: &Clock) -> Self {
459        Self {
460            node_pubkey: vote_init.node_pubkey,
461            authorized_voters: AuthorizedVoters::new(clock.epoch, vote_init.authorized_voter),
462            authorized_withdrawer: vote_init.authorized_withdrawer,
463            commission: vote_init.commission,
464            ..VoteState::default()
465        }
466    }
467
468    pub fn new_rand_for_tests(node_pubkey: Pubkey, root_slot: Slot) -> Self {
469        let votes = (1..32)
470            .map(|x| LandedVote {
471                latency: 0,
472                lockout: Lockout::new_with_confirmation_count(
473                    u64::from(x).saturating_add(root_slot),
474                    32_u32.saturating_sub(x),
475                ),
476            })
477            .collect();
478        Self {
479            node_pubkey,
480            root_slot: Some(root_slot),
481            votes,
482            ..VoteState::default()
483        }
484    }
485
486    pub fn get_authorized_voter(&self, epoch: Epoch) -> Option<Pubkey> {
487        self.authorized_voters.get_authorized_voter(epoch)
488    }
489
490    pub fn authorized_voters(&self) -> &AuthorizedVoters {
491        &self.authorized_voters
492    }
493
494    pub fn prior_voters(&mut self) -> &CircBuf<(Pubkey, Epoch, Epoch)> {
495        &self.prior_voters
496    }
497
498    pub fn get_rent_exempt_reserve(rent: &Rent) -> u64 {
499        rent.minimum_balance(VoteState::size_of())
500    }
501
502    /// Upper limit on the size of the Vote State
503    /// when votes.len() is MAX_LOCKOUT_HISTORY.
504    pub const fn size_of() -> usize {
505        3762 // see test_vote_state_size_of.
506    }
507
508    // NOTE we retain `bincode::deserialize` for `not(target_os = "solana")` pending testing on mainnet-beta
509    // once that testing is done, `VoteState::deserialize_into` may be used for all targets
510    // conversion of V0_23_5 to current must be handled specially, however
511    // because it inserts a null voter into `authorized_voters`
512    // which `VoteStateVersions::is_uninitialized` erroneously reports as initialized
513    #[cfg(any(target_os = "solana", feature = "bincode"))]
514    pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
515        #[cfg(not(target_os = "solana"))]
516        {
517            deserialize::<VoteStateVersions>(input)
518                .map(|versioned| versioned.convert_to_current())
519                .map_err(|_| InstructionError::InvalidAccountData)
520        }
521        #[cfg(target_os = "solana")]
522        {
523            let mut vote_state = Self::default();
524            Self::deserialize_into(input, &mut vote_state)?;
525            Ok(vote_state)
526        }
527    }
528
529    /// Deserializes the input `VoteStateVersions` buffer directly into the provided `VoteState`.
530    ///
531    /// In a SBPF context, V0_23_5 is not supported, but in non-SBPF, all versions are supported for
532    /// compatibility with `bincode::deserialize`.
533    ///
534    /// On success, `vote_state` reflects the state of the input data. On failure, `vote_state` is
535    /// reset to `VoteState::default()`.
536    #[cfg(any(target_os = "solana", feature = "bincode"))]
537    pub fn deserialize_into(
538        input: &[u8],
539        vote_state: &mut VoteState,
540    ) -> Result<(), InstructionError> {
541        // Rebind vote_state to *mut VoteState so that the &mut binding isn't
542        // accessible anymore, preventing accidental use after this point.
543        //
544        // NOTE: switch to ptr::from_mut() once platform-tools moves to rustc >= 1.76
545        let vote_state = vote_state as *mut VoteState;
546
547        // Safety: vote_state is valid to_drop (see drop_in_place() docs). After
548        // dropping, the pointer is treated as uninitialized and only accessed
549        // through ptr::write, which is safe as per drop_in_place docs.
550        unsafe {
551            std::ptr::drop_in_place(vote_state);
552        }
553
554        // This is to reset vote_state to VoteState::default() if deserialize fails or panics.
555        struct DropGuard {
556            vote_state: *mut VoteState,
557        }
558
559        impl Drop for DropGuard {
560            fn drop(&mut self) {
561                // Safety:
562                //
563                // Deserialize failed or panicked so at this point vote_state is uninitialized. We
564                // must write a new _valid_ value into it or after returning (or unwinding) from
565                // this function the caller is left with an uninitialized `&mut VoteState`, which is
566                // UB (references must always be valid).
567                //
568                // This is always safe and doesn't leak memory because deserialize_into_ptr() writes
569                // into the fields that heap alloc only when it returns Ok().
570                unsafe {
571                    self.vote_state.write(VoteState::default());
572                }
573            }
574        }
575
576        let guard = DropGuard { vote_state };
577
578        let res = VoteState::deserialize_into_ptr(input, vote_state);
579        if res.is_ok() {
580            std::mem::forget(guard);
581        }
582
583        res
584    }
585
586    /// Deserializes the input `VoteStateVersions` buffer directly into the provided
587    /// `MaybeUninit<VoteState>`.
588    ///
589    /// In a SBPF context, V0_23_5 is not supported, but in non-SBPF, all versions are supported for
590    /// compatibility with `bincode::deserialize`.
591    ///
592    /// On success, `vote_state` is fully initialized and can be converted to `VoteState` using
593    /// [MaybeUninit::assume_init]. On failure, `vote_state` may still be uninitialized and must not
594    /// be converted to `VoteState`.
595    #[cfg(any(target_os = "solana", feature = "bincode"))]
596    pub fn deserialize_into_uninit(
597        input: &[u8],
598        vote_state: &mut std::mem::MaybeUninit<VoteState>,
599    ) -> Result<(), InstructionError> {
600        VoteState::deserialize_into_ptr(input, vote_state.as_mut_ptr())
601    }
602
603    #[cfg(any(target_os = "solana", feature = "bincode"))]
604    fn deserialize_into_ptr(
605        input: &[u8],
606        vote_state: *mut VoteState,
607    ) -> Result<(), InstructionError> {
608        let mut cursor = std::io::Cursor::new(input);
609
610        let variant = solana_serialize_utils::cursor::read_u32(&mut cursor)?;
611        match variant {
612            // V0_23_5. not supported for bpf targets; these should not exist on mainnet
613            // supported for non-bpf targets for backwards compatibility
614            0 => {
615                #[cfg(not(target_os = "solana"))]
616                {
617                    // Safety: vote_state is valid as it comes from `&mut MaybeUninit<VoteState>` or
618                    // `&mut VoteState`. In the first case, the value is uninitialized so we write()
619                    // to avoid dropping invalid data; in the latter case, we `drop_in_place()`
620                    // before writing so the value has already been dropped and we just write a new
621                    // one in place.
622                    unsafe {
623                        vote_state.write(
624                            bincode::deserialize::<VoteStateVersions>(input)
625                                .map(|versioned| versioned.convert_to_current())
626                                .map_err(|_| InstructionError::InvalidAccountData)?,
627                        );
628                    }
629                    Ok(())
630                }
631                #[cfg(target_os = "solana")]
632                Err(InstructionError::InvalidAccountData)
633            }
634            // V1_14_11. substantially different layout and data from V0_23_5
635            1 => deserialize_vote_state_into(&mut cursor, vote_state, false),
636            // Current. the only difference from V1_14_11 is the addition of a slot-latency to each vote
637            2 => deserialize_vote_state_into(&mut cursor, vote_state, true),
638            _ => Err(InstructionError::InvalidAccountData),
639        }?;
640
641        Ok(())
642    }
643
644    #[cfg(feature = "bincode")]
645    pub fn serialize(
646        versioned: &VoteStateVersions,
647        output: &mut [u8],
648    ) -> Result<(), InstructionError> {
649        serialize_into(output, versioned).map_err(|err| match *err {
650            ErrorKind::SizeLimit => InstructionError::AccountDataTooSmall,
651            _ => InstructionError::GenericError,
652        })
653    }
654
655    /// returns commission split as (voter_portion, staker_portion, was_split) tuple
656    ///
657    ///  if commission calculation is 100% one way or other,
658    ///   indicate with false for was_split
659    #[deprecated(since = "2.2.0", note = "logic was moved into the agave runtime crate")]
660    pub fn commission_split(&self, on: u64) -> (u64, u64, bool) {
661        match self.commission.min(100) {
662            0 => (0, on, false),
663            100 => (on, 0, false),
664            split => {
665                let on = u128::from(on);
666                // Calculate mine and theirs independently and symmetrically instead of
667                // using the remainder of the other to treat them strictly equally.
668                // This is also to cancel the rewarding if either of the parties
669                // should receive only fractional lamports, resulting in not being rewarded at all.
670                // Thus, note that we intentionally discard any residual fractional lamports.
671                let mine = on
672                    .checked_mul(u128::from(split))
673                    .expect("multiplication of a u64 and u8 should not overflow")
674                    / 100u128;
675                let theirs = on
676                    .checked_mul(u128::from(
677                        100u8
678                            .checked_sub(split)
679                            .expect("commission cannot be greater than 100"),
680                    ))
681                    .expect("multiplication of a u64 and u8 should not overflow")
682                    / 100u128;
683
684                (mine as u64, theirs as u64, true)
685            }
686        }
687    }
688
689    /// Returns if the vote state contains a slot `candidate_slot`
690    pub fn contains_slot(&self, candidate_slot: Slot) -> bool {
691        self.votes
692            .binary_search_by(|vote| vote.slot().cmp(&candidate_slot))
693            .is_ok()
694    }
695
696    #[cfg(test)]
697    fn get_max_sized_vote_state() -> VoteState {
698        let mut authorized_voters = AuthorizedVoters::default();
699        for i in 0..=MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
700            authorized_voters.insert(i, Pubkey::new_unique());
701        }
702
703        VoteState {
704            votes: VecDeque::from(vec![LandedVote::default(); MAX_LOCKOUT_HISTORY]),
705            root_slot: Some(u64::MAX),
706            epoch_credits: vec![(0, 0, 0); MAX_EPOCH_CREDITS_HISTORY],
707            authorized_voters,
708            ..Self::default()
709        }
710    }
711
712    pub fn process_next_vote_slot(
713        &mut self,
714        next_vote_slot: Slot,
715        epoch: Epoch,
716        current_slot: Slot,
717    ) {
718        // Ignore votes for slots earlier than we already have votes for
719        if self
720            .last_voted_slot()
721            .is_some_and(|last_voted_slot| next_vote_slot <= last_voted_slot)
722        {
723            return;
724        }
725
726        self.pop_expired_votes(next_vote_slot);
727
728        let landed_vote = LandedVote {
729            latency: Self::compute_vote_latency(next_vote_slot, current_slot),
730            lockout: Lockout::new(next_vote_slot),
731        };
732
733        // Once the stack is full, pop the oldest lockout and distribute rewards
734        if self.votes.len() == MAX_LOCKOUT_HISTORY {
735            let credits = self.credits_for_vote_at_index(0);
736            let landed_vote = self.votes.pop_front().unwrap();
737            self.root_slot = Some(landed_vote.slot());
738
739            self.increment_credits(epoch, credits);
740        }
741        self.votes.push_back(landed_vote);
742        self.double_lockouts();
743    }
744
745    /// increment credits, record credits for last epoch if new epoch
746    pub fn increment_credits(&mut self, epoch: Epoch, credits: u64) {
747        // increment credits, record by epoch
748
749        // never seen a credit
750        if self.epoch_credits.is_empty() {
751            self.epoch_credits.push((epoch, 0, 0));
752        } else if epoch != self.epoch_credits.last().unwrap().0 {
753            let (_, credits, prev_credits) = *self.epoch_credits.last().unwrap();
754
755            if credits != prev_credits {
756                // if credits were earned previous epoch
757                // append entry at end of list for the new epoch
758                self.epoch_credits.push((epoch, credits, credits));
759            } else {
760                // else just move the current epoch
761                self.epoch_credits.last_mut().unwrap().0 = epoch;
762            }
763
764            // Remove too old epoch_credits
765            if self.epoch_credits.len() > MAX_EPOCH_CREDITS_HISTORY {
766                self.epoch_credits.remove(0);
767            }
768        }
769
770        self.epoch_credits.last_mut().unwrap().1 =
771            self.epoch_credits.last().unwrap().1.saturating_add(credits);
772    }
773
774    // Computes the vote latency for vote on voted_for_slot where the vote itself landed in current_slot
775    pub fn compute_vote_latency(voted_for_slot: Slot, current_slot: Slot) -> u8 {
776        std::cmp::min(current_slot.saturating_sub(voted_for_slot), u8::MAX as u64) as u8
777    }
778
779    /// Returns the credits to award for a vote at the given lockout slot index
780    pub fn credits_for_vote_at_index(&self, index: usize) -> u64 {
781        let latency = self
782            .votes
783            .get(index)
784            .map_or(0, |landed_vote| landed_vote.latency);
785
786        // If latency is 0, this means that the Lockout was created and stored from a software version that did not
787        // store vote latencies; in this case, 1 credit is awarded
788        if latency == 0 {
789            1
790        } else {
791            match latency.checked_sub(VOTE_CREDITS_GRACE_SLOTS) {
792                None | Some(0) => {
793                    // latency was <= VOTE_CREDITS_GRACE_SLOTS, so maximum credits are awarded
794                    VOTE_CREDITS_MAXIMUM_PER_SLOT as u64
795                }
796
797                Some(diff) => {
798                    // diff = latency - VOTE_CREDITS_GRACE_SLOTS, and diff > 0
799                    // Subtract diff from VOTE_CREDITS_MAXIMUM_PER_SLOT which is the number of credits to award
800                    match VOTE_CREDITS_MAXIMUM_PER_SLOT.checked_sub(diff) {
801                        // If diff >= VOTE_CREDITS_MAXIMUM_PER_SLOT, 1 credit is awarded
802                        None | Some(0) => 1,
803
804                        Some(credits) => credits as u64,
805                    }
806                }
807            }
808        }
809    }
810
811    pub fn nth_recent_lockout(&self, position: usize) -> Option<&Lockout> {
812        if position < self.votes.len() {
813            let pos = self
814                .votes
815                .len()
816                .checked_sub(position)
817                .and_then(|pos| pos.checked_sub(1))?;
818            self.votes.get(pos).map(|vote| &vote.lockout)
819        } else {
820            None
821        }
822    }
823
824    pub fn last_lockout(&self) -> Option<&Lockout> {
825        self.votes.back().map(|vote| &vote.lockout)
826    }
827
828    pub fn last_voted_slot(&self) -> Option<Slot> {
829        self.last_lockout().map(|v| v.slot())
830    }
831
832    // Upto MAX_LOCKOUT_HISTORY many recent unexpired
833    // vote slots pushed onto the stack.
834    pub fn tower(&self) -> Vec<Slot> {
835        self.votes.iter().map(|v| v.slot()).collect()
836    }
837
838    pub fn current_epoch(&self) -> Epoch {
839        if self.epoch_credits.is_empty() {
840            0
841        } else {
842            self.epoch_credits.last().unwrap().0
843        }
844    }
845
846    /// Number of "credits" owed to this account from the mining pool. Submit this
847    /// VoteState to the Rewards program to trade credits for lamports.
848    pub fn credits(&self) -> u64 {
849        if self.epoch_credits.is_empty() {
850            0
851        } else {
852            self.epoch_credits.last().unwrap().1
853        }
854    }
855
856    /// Number of "credits" owed to this account from the mining pool on a per-epoch basis,
857    ///  starting from credits observed.
858    /// Each tuple of (Epoch, u64, u64) is read as (epoch, credits, prev_credits), where
859    ///   credits for each epoch is credits - prev_credits; while redundant this makes
860    ///   calculating rewards over partial epochs nice and simple
861    pub fn epoch_credits(&self) -> &Vec<(Epoch, u64, u64)> {
862        &self.epoch_credits
863    }
864
865    pub fn set_new_authorized_voter<F>(
866        &mut self,
867        authorized_pubkey: &Pubkey,
868        current_epoch: Epoch,
869        target_epoch: Epoch,
870        verify: F,
871    ) -> Result<(), InstructionError>
872    where
873        F: Fn(Pubkey) -> Result<(), InstructionError>,
874    {
875        let epoch_authorized_voter = self.get_and_update_authorized_voter(current_epoch)?;
876        verify(epoch_authorized_voter)?;
877
878        // The offset in slots `n` on which the target_epoch
879        // (default value `DEFAULT_LEADER_SCHEDULE_SLOT_OFFSET`) is
880        // calculated is the number of slots available from the
881        // first slot `S` of an epoch in which to set a new voter for
882        // the epoch at `S` + `n`
883        if self.authorized_voters.contains(target_epoch) {
884            return Err(VoteError::TooSoonToReauthorize.into());
885        }
886
887        // Get the latest authorized_voter
888        let (latest_epoch, latest_authorized_pubkey) = self
889            .authorized_voters
890            .last()
891            .ok_or(InstructionError::InvalidAccountData)?;
892
893        // If we're not setting the same pubkey as authorized pubkey again,
894        // then update the list of prior voters to mark the expiration
895        // of the old authorized pubkey
896        if latest_authorized_pubkey != authorized_pubkey {
897            // Update the epoch ranges of authorized pubkeys that will be expired
898            let epoch_of_last_authorized_switch =
899                self.prior_voters.last().map(|range| range.2).unwrap_or(0);
900
901            // target_epoch must:
902            // 1) Be monotonically increasing due to the clock always
903            //    moving forward
904            // 2) not be equal to latest epoch otherwise this
905            //    function would have returned TooSoonToReauthorize error
906            //    above
907            if target_epoch <= *latest_epoch {
908                return Err(InstructionError::InvalidAccountData);
909            }
910
911            // Commit the new state
912            self.prior_voters.append((
913                *latest_authorized_pubkey,
914                epoch_of_last_authorized_switch,
915                target_epoch,
916            ));
917        }
918
919        self.authorized_voters
920            .insert(target_epoch, *authorized_pubkey);
921
922        Ok(())
923    }
924
925    pub fn get_and_update_authorized_voter(
926        &mut self,
927        current_epoch: Epoch,
928    ) -> Result<Pubkey, InstructionError> {
929        let pubkey = self
930            .authorized_voters
931            .get_and_cache_authorized_voter_for_epoch(current_epoch)
932            .ok_or(InstructionError::InvalidAccountData)?;
933        self.authorized_voters
934            .purge_authorized_voters(current_epoch);
935        Ok(pubkey)
936    }
937
938    // Pop all recent votes that are not locked out at the next vote slot.  This
939    // allows validators to switch forks once their votes for another fork have
940    // expired. This also allows validators continue voting on recent blocks in
941    // the same fork without increasing lockouts.
942    pub fn pop_expired_votes(&mut self, next_vote_slot: Slot) {
943        while let Some(vote) = self.last_lockout() {
944            if !vote.is_locked_out_at_slot(next_vote_slot) {
945                self.votes.pop_back();
946            } else {
947                break;
948            }
949        }
950    }
951
952    pub fn double_lockouts(&mut self) {
953        let stack_depth = self.votes.len();
954        for (i, v) in self.votes.iter_mut().enumerate() {
955            // Don't increase the lockout for this vote until we get more confirmations
956            // than the max number of confirmations this vote has seen
957            if stack_depth >
958                i.checked_add(v.confirmation_count() as usize)
959                    .expect("`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`")
960            {
961                v.lockout.increase_confirmation_count(1);
962            }
963        }
964    }
965
966    pub fn process_timestamp(
967        &mut self,
968        slot: Slot,
969        timestamp: UnixTimestamp,
970    ) -> Result<(), VoteError> {
971        if (slot < self.last_timestamp.slot || timestamp < self.last_timestamp.timestamp)
972            || (slot == self.last_timestamp.slot
973                && BlockTimestamp { slot, timestamp } != self.last_timestamp
974                && self.last_timestamp.slot != 0)
975        {
976            return Err(VoteError::TimestampTooOld);
977        }
978        self.last_timestamp = BlockTimestamp { slot, timestamp };
979        Ok(())
980    }
981
982    pub fn is_correct_size_and_initialized(data: &[u8]) -> bool {
983        const VERSION_OFFSET: usize = 4;
984        const DEFAULT_PRIOR_VOTERS_END: usize = VERSION_OFFSET + DEFAULT_PRIOR_VOTERS_OFFSET;
985        data.len() == VoteState::size_of()
986            && data[VERSION_OFFSET..DEFAULT_PRIOR_VOTERS_END] != [0; DEFAULT_PRIOR_VOTERS_OFFSET]
987    }
988}
989
990#[cfg(feature = "serde")]
991pub mod serde_compact_vote_state_update {
992    use {
993        super::*,
994        crate::state::Lockout,
995        serde::{Deserialize, Deserializer, Serialize, Serializer},
996        solana_serde_varint as serde_varint, solana_short_vec as short_vec,
997    };
998
999    #[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
1000    #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
1001    struct LockoutOffset {
1002        #[serde(with = "serde_varint")]
1003        offset: Slot,
1004        confirmation_count: u8,
1005    }
1006
1007    #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
1008    struct CompactVoteStateUpdate {
1009        root: Slot,
1010        #[serde(with = "short_vec")]
1011        lockout_offsets: Vec<LockoutOffset>,
1012        hash: Hash,
1013        timestamp: Option<UnixTimestamp>,
1014    }
1015
1016    pub fn serialize<S>(
1017        vote_state_update: &VoteStateUpdate,
1018        serializer: S,
1019    ) -> Result<S::Ok, S::Error>
1020    where
1021        S: Serializer,
1022    {
1023        let lockout_offsets = vote_state_update.lockouts.iter().scan(
1024            vote_state_update.root.unwrap_or_default(),
1025            |slot, lockout| {
1026                let Some(offset) = lockout.slot().checked_sub(*slot) else {
1027                    return Some(Err(serde::ser::Error::custom("Invalid vote lockout")));
1028                };
1029                let Ok(confirmation_count) = u8::try_from(lockout.confirmation_count()) else {
1030                    return Some(Err(serde::ser::Error::custom("Invalid confirmation count")));
1031                };
1032                let lockout_offset = LockoutOffset {
1033                    offset,
1034                    confirmation_count,
1035                };
1036                *slot = lockout.slot();
1037                Some(Ok(lockout_offset))
1038            },
1039        );
1040        let compact_vote_state_update = CompactVoteStateUpdate {
1041            root: vote_state_update.root.unwrap_or(Slot::MAX),
1042            lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
1043            hash: vote_state_update.hash,
1044            timestamp: vote_state_update.timestamp,
1045        };
1046        compact_vote_state_update.serialize(serializer)
1047    }
1048
1049    pub fn deserialize<'de, D>(deserializer: D) -> Result<VoteStateUpdate, D::Error>
1050    where
1051        D: Deserializer<'de>,
1052    {
1053        let CompactVoteStateUpdate {
1054            root,
1055            lockout_offsets,
1056            hash,
1057            timestamp,
1058        } = CompactVoteStateUpdate::deserialize(deserializer)?;
1059        let root = (root != Slot::MAX).then_some(root);
1060        let lockouts =
1061            lockout_offsets
1062                .iter()
1063                .scan(root.unwrap_or_default(), |slot, lockout_offset| {
1064                    *slot = match slot.checked_add(lockout_offset.offset) {
1065                        None => {
1066                            return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
1067                        }
1068                        Some(slot) => slot,
1069                    };
1070                    let lockout = Lockout::new_with_confirmation_count(
1071                        *slot,
1072                        u32::from(lockout_offset.confirmation_count),
1073                    );
1074                    Some(Ok(lockout))
1075                });
1076        Ok(VoteStateUpdate {
1077            root,
1078            lockouts: lockouts.collect::<Result<_, _>>()?,
1079            hash,
1080            timestamp,
1081        })
1082    }
1083}
1084
1085#[cfg(feature = "serde")]
1086pub mod serde_tower_sync {
1087    use {
1088        super::*,
1089        crate::state::Lockout,
1090        serde::{Deserialize, Deserializer, Serialize, Serializer},
1091        solana_serde_varint as serde_varint, solana_short_vec as short_vec,
1092    };
1093
1094    #[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
1095    #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
1096    struct LockoutOffset {
1097        #[serde(with = "serde_varint")]
1098        offset: Slot,
1099        confirmation_count: u8,
1100    }
1101
1102    #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
1103    struct CompactTowerSync {
1104        root: Slot,
1105        #[serde(with = "short_vec")]
1106        lockout_offsets: Vec<LockoutOffset>,
1107        hash: Hash,
1108        timestamp: Option<UnixTimestamp>,
1109        block_id: Hash,
1110    }
1111
1112    pub fn serialize<S>(tower_sync: &TowerSync, serializer: S) -> Result<S::Ok, S::Error>
1113    where
1114        S: Serializer,
1115    {
1116        let lockout_offsets = tower_sync.lockouts.iter().scan(
1117            tower_sync.root.unwrap_or_default(),
1118            |slot, lockout| {
1119                let Some(offset) = lockout.slot().checked_sub(*slot) else {
1120                    return Some(Err(serde::ser::Error::custom("Invalid vote lockout")));
1121                };
1122                let Ok(confirmation_count) = u8::try_from(lockout.confirmation_count()) else {
1123                    return Some(Err(serde::ser::Error::custom("Invalid confirmation count")));
1124                };
1125                let lockout_offset = LockoutOffset {
1126                    offset,
1127                    confirmation_count,
1128                };
1129                *slot = lockout.slot();
1130                Some(Ok(lockout_offset))
1131            },
1132        );
1133        let compact_tower_sync = CompactTowerSync {
1134            root: tower_sync.root.unwrap_or(Slot::MAX),
1135            lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
1136            hash: tower_sync.hash,
1137            timestamp: tower_sync.timestamp,
1138            block_id: tower_sync.block_id,
1139        };
1140        compact_tower_sync.serialize(serializer)
1141    }
1142
1143    pub fn deserialize<'de, D>(deserializer: D) -> Result<TowerSync, D::Error>
1144    where
1145        D: Deserializer<'de>,
1146    {
1147        let CompactTowerSync {
1148            root,
1149            lockout_offsets,
1150            hash,
1151            timestamp,
1152            block_id,
1153        } = CompactTowerSync::deserialize(deserializer)?;
1154        let root = (root != Slot::MAX).then_some(root);
1155        let lockouts =
1156            lockout_offsets
1157                .iter()
1158                .scan(root.unwrap_or_default(), |slot, lockout_offset| {
1159                    *slot = match slot.checked_add(lockout_offset.offset) {
1160                        None => {
1161                            return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
1162                        }
1163                        Some(slot) => slot,
1164                    };
1165                    let lockout = Lockout::new_with_confirmation_count(
1166                        *slot,
1167                        u32::from(lockout_offset.confirmation_count),
1168                    );
1169                    Some(Ok(lockout))
1170                });
1171        Ok(TowerSync {
1172            root,
1173            lockouts: lockouts.collect::<Result<_, _>>()?,
1174            hash,
1175            timestamp,
1176            block_id,
1177        })
1178    }
1179}
1180
1181#[cfg(test)]
1182mod tests {
1183    use {
1184        super::*, bincode::serialized_size, core::mem::MaybeUninit, itertools::Itertools, rand::Rng,
1185    };
1186
1187    #[test]
1188    fn test_vote_serialize() {
1189        let mut buffer: Vec<u8> = vec![0; VoteState::size_of()];
1190        let mut vote_state = VoteState::default();
1191        vote_state
1192            .votes
1193            .resize(MAX_LOCKOUT_HISTORY, LandedVote::default());
1194        vote_state.root_slot = Some(1);
1195        let versioned = VoteStateVersions::new_current(vote_state);
1196        assert!(VoteState::serialize(&versioned, &mut buffer[0..4]).is_err());
1197        VoteState::serialize(&versioned, &mut buffer).unwrap();
1198        assert_eq!(
1199            VoteState::deserialize(&buffer).unwrap(),
1200            versioned.convert_to_current()
1201        );
1202    }
1203
1204    #[test]
1205    fn test_vote_deserialize_into() {
1206        // base case
1207        let target_vote_state = VoteState::default();
1208        let vote_state_buf =
1209            bincode::serialize(&VoteStateVersions::new_current(target_vote_state.clone())).unwrap();
1210
1211        let mut test_vote_state = VoteState::default();
1212        VoteState::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
1213
1214        assert_eq!(target_vote_state, test_vote_state);
1215
1216        // variant
1217        // provide 4x the minimum struct size in bytes to ensure we typically touch every field
1218        let struct_bytes_x4 = std::mem::size_of::<VoteState>() * 4;
1219        for _ in 0..1000 {
1220            let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
1221            let mut unstructured = Unstructured::new(&raw_data);
1222
1223            let target_vote_state_versions =
1224                VoteStateVersions::arbitrary(&mut unstructured).unwrap();
1225            let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
1226            let target_vote_state = target_vote_state_versions.convert_to_current();
1227
1228            let mut test_vote_state = VoteState::default();
1229            VoteState::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
1230
1231            assert_eq!(target_vote_state, test_vote_state);
1232        }
1233    }
1234
1235    #[test]
1236    fn test_vote_deserialize_into_error() {
1237        let target_vote_state = VoteState::new_rand_for_tests(Pubkey::new_unique(), 42);
1238        let mut vote_state_buf =
1239            bincode::serialize(&VoteStateVersions::new_current(target_vote_state.clone())).unwrap();
1240        let len = vote_state_buf.len();
1241        vote_state_buf.truncate(len - 1);
1242
1243        let mut test_vote_state = VoteState::default();
1244        VoteState::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap_err();
1245        assert_eq!(test_vote_state, VoteState::default());
1246    }
1247
1248    #[test]
1249    fn test_vote_deserialize_into_uninit() {
1250        // base case
1251        let target_vote_state = VoteState::default();
1252        let vote_state_buf =
1253            bincode::serialize(&VoteStateVersions::new_current(target_vote_state.clone())).unwrap();
1254
1255        let mut test_vote_state = MaybeUninit::uninit();
1256        VoteState::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state).unwrap();
1257        let test_vote_state = unsafe { test_vote_state.assume_init() };
1258
1259        assert_eq!(target_vote_state, test_vote_state);
1260
1261        // variant
1262        // provide 4x the minimum struct size in bytes to ensure we typically touch every field
1263        let struct_bytes_x4 = std::mem::size_of::<VoteState>() * 4;
1264        for _ in 0..1000 {
1265            let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
1266            let mut unstructured = Unstructured::new(&raw_data);
1267
1268            let target_vote_state_versions =
1269                VoteStateVersions::arbitrary(&mut unstructured).unwrap();
1270            let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
1271            let target_vote_state = target_vote_state_versions.convert_to_current();
1272
1273            let mut test_vote_state = MaybeUninit::uninit();
1274            VoteState::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state).unwrap();
1275            let test_vote_state = unsafe { test_vote_state.assume_init() };
1276
1277            assert_eq!(target_vote_state, test_vote_state);
1278        }
1279    }
1280
1281    #[test]
1282    fn test_vote_deserialize_into_uninit_nopanic() {
1283        // base case
1284        let mut test_vote_state = MaybeUninit::uninit();
1285        let e = VoteState::deserialize_into_uninit(&[], &mut test_vote_state).unwrap_err();
1286        assert_eq!(e, InstructionError::InvalidAccountData);
1287
1288        // variant
1289        let serialized_len_x4 = serialized_size(&VoteState::default()).unwrap() * 4;
1290        let mut rng = rand::thread_rng();
1291        for _ in 0..1000 {
1292            let raw_data_length = rng.gen_range(1..serialized_len_x4);
1293            let mut raw_data: Vec<u8> = (0..raw_data_length).map(|_| rng.gen::<u8>()).collect();
1294
1295            // pure random data will ~never have a valid enum tag, so lets help it out
1296            if raw_data_length >= 4 && rng.gen::<bool>() {
1297                let tag = rng.gen::<u8>() % 3;
1298                raw_data[0] = tag;
1299                raw_data[1] = 0;
1300                raw_data[2] = 0;
1301                raw_data[3] = 0;
1302            }
1303
1304            // it is extremely improbable, though theoretically possible, for random bytes to be syntactically valid
1305            // so we only check that the parser does not panic and that it succeeds or fails exactly in line with bincode
1306            let mut test_vote_state = MaybeUninit::uninit();
1307            let test_res = VoteState::deserialize_into_uninit(&raw_data, &mut test_vote_state);
1308            let bincode_res = bincode::deserialize::<VoteStateVersions>(&raw_data)
1309                .map(|versioned| versioned.convert_to_current());
1310
1311            if test_res.is_err() {
1312                assert!(bincode_res.is_err());
1313            } else {
1314                let test_vote_state = unsafe { test_vote_state.assume_init() };
1315                assert_eq!(test_vote_state, bincode_res.unwrap());
1316            }
1317        }
1318    }
1319
1320    #[test]
1321    fn test_vote_deserialize_into_uninit_ill_sized() {
1322        // provide 4x the minimum struct size in bytes to ensure we typically touch every field
1323        let struct_bytes_x4 = std::mem::size_of::<VoteState>() * 4;
1324        for _ in 0..1000 {
1325            let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
1326            let mut unstructured = Unstructured::new(&raw_data);
1327
1328            let original_vote_state_versions =
1329                VoteStateVersions::arbitrary(&mut unstructured).unwrap();
1330            let original_buf = bincode::serialize(&original_vote_state_versions).unwrap();
1331
1332            let mut truncated_buf = original_buf.clone();
1333            let mut expanded_buf = original_buf.clone();
1334
1335            truncated_buf.resize(original_buf.len() - 8, 0);
1336            expanded_buf.resize(original_buf.len() + 8, 0);
1337
1338            // truncated fails
1339            let mut test_vote_state = MaybeUninit::uninit();
1340            let test_res = VoteState::deserialize_into_uninit(&truncated_buf, &mut test_vote_state);
1341            let bincode_res = bincode::deserialize::<VoteStateVersions>(&truncated_buf)
1342                .map(|versioned| versioned.convert_to_current());
1343
1344            assert!(test_res.is_err());
1345            assert!(bincode_res.is_err());
1346
1347            // expanded succeeds
1348            let mut test_vote_state = MaybeUninit::uninit();
1349            VoteState::deserialize_into_uninit(&expanded_buf, &mut test_vote_state).unwrap();
1350            let bincode_res = bincode::deserialize::<VoteStateVersions>(&expanded_buf)
1351                .map(|versioned| versioned.convert_to_current());
1352
1353            let test_vote_state = unsafe { test_vote_state.assume_init() };
1354            assert_eq!(test_vote_state, bincode_res.unwrap());
1355        }
1356    }
1357
1358    #[test]
1359    #[allow(deprecated)]
1360    fn test_vote_state_commission_split() {
1361        let vote_state = VoteState::default();
1362
1363        assert_eq!(vote_state.commission_split(1), (0, 1, false));
1364
1365        let mut vote_state = VoteState {
1366            commission: u8::MAX,
1367            ..VoteState::default()
1368        };
1369        assert_eq!(vote_state.commission_split(1), (1, 0, false));
1370
1371        vote_state.commission = 99;
1372        assert_eq!(vote_state.commission_split(10), (9, 0, true));
1373
1374        vote_state.commission = 1;
1375        assert_eq!(vote_state.commission_split(10), (0, 9, true));
1376
1377        vote_state.commission = 50;
1378        let (voter_portion, staker_portion, was_split) = vote_state.commission_split(10);
1379
1380        assert_eq!((voter_portion, staker_portion, was_split), (5, 5, true));
1381    }
1382
1383    #[test]
1384    fn test_vote_state_epoch_credits() {
1385        let mut vote_state = VoteState::default();
1386
1387        assert_eq!(vote_state.credits(), 0);
1388        assert_eq!(vote_state.epoch_credits().clone(), vec![]);
1389
1390        let mut expected = vec![];
1391        let mut credits = 0;
1392        let epochs = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
1393        for epoch in 0..epochs {
1394            for _j in 0..epoch {
1395                vote_state.increment_credits(epoch, 1);
1396                credits += 1;
1397            }
1398            expected.push((epoch, credits, credits - epoch));
1399        }
1400
1401        while expected.len() > MAX_EPOCH_CREDITS_HISTORY {
1402            expected.remove(0);
1403        }
1404
1405        assert_eq!(vote_state.credits(), credits);
1406        assert_eq!(vote_state.epoch_credits().clone(), expected);
1407    }
1408
1409    #[test]
1410    fn test_vote_state_epoch0_no_credits() {
1411        let mut vote_state = VoteState::default();
1412
1413        assert_eq!(vote_state.epoch_credits().len(), 0);
1414        vote_state.increment_credits(1, 1);
1415        assert_eq!(vote_state.epoch_credits().len(), 1);
1416
1417        vote_state.increment_credits(2, 1);
1418        assert_eq!(vote_state.epoch_credits().len(), 2);
1419    }
1420
1421    #[test]
1422    fn test_vote_state_increment_credits() {
1423        let mut vote_state = VoteState::default();
1424
1425        let credits = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
1426        for i in 0..credits {
1427            vote_state.increment_credits(i, 1);
1428        }
1429        assert_eq!(vote_state.credits(), credits);
1430        assert!(vote_state.epoch_credits().len() <= MAX_EPOCH_CREDITS_HISTORY);
1431    }
1432
1433    #[test]
1434    fn test_vote_process_timestamp() {
1435        let (slot, timestamp) = (15, 1_575_412_285);
1436        let mut vote_state = VoteState {
1437            last_timestamp: BlockTimestamp { slot, timestamp },
1438            ..VoteState::default()
1439        };
1440
1441        assert_eq!(
1442            vote_state.process_timestamp(slot - 1, timestamp + 1),
1443            Err(VoteError::TimestampTooOld)
1444        );
1445        assert_eq!(
1446            vote_state.last_timestamp,
1447            BlockTimestamp { slot, timestamp }
1448        );
1449        assert_eq!(
1450            vote_state.process_timestamp(slot + 1, timestamp - 1),
1451            Err(VoteError::TimestampTooOld)
1452        );
1453        assert_eq!(
1454            vote_state.process_timestamp(slot, timestamp + 1),
1455            Err(VoteError::TimestampTooOld)
1456        );
1457        assert_eq!(vote_state.process_timestamp(slot, timestamp), Ok(()));
1458        assert_eq!(
1459            vote_state.last_timestamp,
1460            BlockTimestamp { slot, timestamp }
1461        );
1462        assert_eq!(vote_state.process_timestamp(slot + 1, timestamp), Ok(()));
1463        assert_eq!(
1464            vote_state.last_timestamp,
1465            BlockTimestamp {
1466                slot: slot + 1,
1467                timestamp
1468            }
1469        );
1470        assert_eq!(
1471            vote_state.process_timestamp(slot + 2, timestamp + 1),
1472            Ok(())
1473        );
1474        assert_eq!(
1475            vote_state.last_timestamp,
1476            BlockTimestamp {
1477                slot: slot + 2,
1478                timestamp: timestamp + 1
1479            }
1480        );
1481
1482        // Test initial vote
1483        vote_state.last_timestamp = BlockTimestamp::default();
1484        assert_eq!(vote_state.process_timestamp(0, timestamp), Ok(()));
1485    }
1486
1487    #[test]
1488    fn test_get_and_update_authorized_voter() {
1489        let original_voter = Pubkey::new_unique();
1490        let mut vote_state = VoteState::new(
1491            &VoteInit {
1492                node_pubkey: original_voter,
1493                authorized_voter: original_voter,
1494                authorized_withdrawer: original_voter,
1495                commission: 0,
1496            },
1497            &Clock::default(),
1498        );
1499
1500        assert_eq!(vote_state.authorized_voters.len(), 1);
1501        assert_eq!(
1502            *vote_state.authorized_voters.first().unwrap().1,
1503            original_voter
1504        );
1505
1506        // If no new authorized voter was set, the same authorized voter
1507        // is locked into the next epoch
1508        assert_eq!(
1509            vote_state.get_and_update_authorized_voter(1).unwrap(),
1510            original_voter
1511        );
1512
1513        // Try to get the authorized voter for epoch 5, implies
1514        // the authorized voter for epochs 1-4 were unchanged
1515        assert_eq!(
1516            vote_state.get_and_update_authorized_voter(5).unwrap(),
1517            original_voter
1518        );
1519
1520        // Authorized voter for expired epoch 0..5 should have been
1521        // purged and no longer queryable
1522        assert_eq!(vote_state.authorized_voters.len(), 1);
1523        for i in 0..5 {
1524            assert!(vote_state
1525                .authorized_voters
1526                .get_authorized_voter(i)
1527                .is_none());
1528        }
1529
1530        // Set an authorized voter change at slot 7
1531        let new_authorized_voter = Pubkey::new_unique();
1532        vote_state
1533            .set_new_authorized_voter(&new_authorized_voter, 5, 7, |_| Ok(()))
1534            .unwrap();
1535
1536        // Try to get the authorized voter for epoch 6, unchanged
1537        assert_eq!(
1538            vote_state.get_and_update_authorized_voter(6).unwrap(),
1539            original_voter
1540        );
1541
1542        // Try to get the authorized voter for epoch 7 and onwards, should
1543        // be the new authorized voter
1544        for i in 7..10 {
1545            assert_eq!(
1546                vote_state.get_and_update_authorized_voter(i).unwrap(),
1547                new_authorized_voter
1548            );
1549        }
1550        assert_eq!(vote_state.authorized_voters.len(), 1);
1551    }
1552
1553    #[test]
1554    fn test_set_new_authorized_voter() {
1555        let original_voter = Pubkey::new_unique();
1556        let epoch_offset = 15;
1557        let mut vote_state = VoteState::new(
1558            &VoteInit {
1559                node_pubkey: original_voter,
1560                authorized_voter: original_voter,
1561                authorized_withdrawer: original_voter,
1562                commission: 0,
1563            },
1564            &Clock::default(),
1565        );
1566
1567        assert!(vote_state.prior_voters.last().is_none());
1568
1569        let new_voter = Pubkey::new_unique();
1570        // Set a new authorized voter
1571        vote_state
1572            .set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(()))
1573            .unwrap();
1574
1575        assert_eq!(vote_state.prior_voters.idx, 0);
1576        assert_eq!(
1577            vote_state.prior_voters.last(),
1578            Some(&(original_voter, 0, epoch_offset))
1579        );
1580
1581        // Trying to set authorized voter for same epoch again should fail
1582        assert_eq!(
1583            vote_state.set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(())),
1584            Err(VoteError::TooSoonToReauthorize.into())
1585        );
1586
1587        // Setting the same authorized voter again should succeed
1588        vote_state
1589            .set_new_authorized_voter(&new_voter, 2, 2 + epoch_offset, |_| Ok(()))
1590            .unwrap();
1591
1592        // Set a third and fourth authorized voter
1593        let new_voter2 = Pubkey::new_unique();
1594        vote_state
1595            .set_new_authorized_voter(&new_voter2, 3, 3 + epoch_offset, |_| Ok(()))
1596            .unwrap();
1597        assert_eq!(vote_state.prior_voters.idx, 1);
1598        assert_eq!(
1599            vote_state.prior_voters.last(),
1600            Some(&(new_voter, epoch_offset, 3 + epoch_offset))
1601        );
1602
1603        let new_voter3 = Pubkey::new_unique();
1604        vote_state
1605            .set_new_authorized_voter(&new_voter3, 6, 6 + epoch_offset, |_| Ok(()))
1606            .unwrap();
1607        assert_eq!(vote_state.prior_voters.idx, 2);
1608        assert_eq!(
1609            vote_state.prior_voters.last(),
1610            Some(&(new_voter2, 3 + epoch_offset, 6 + epoch_offset))
1611        );
1612
1613        // Check can set back to original voter
1614        vote_state
1615            .set_new_authorized_voter(&original_voter, 9, 9 + epoch_offset, |_| Ok(()))
1616            .unwrap();
1617
1618        // Run with these voters for a while, check the ranges of authorized
1619        // voters is correct
1620        for i in 9..epoch_offset {
1621            assert_eq!(
1622                vote_state.get_and_update_authorized_voter(i).unwrap(),
1623                original_voter
1624            );
1625        }
1626        for i in epoch_offset..3 + epoch_offset {
1627            assert_eq!(
1628                vote_state.get_and_update_authorized_voter(i).unwrap(),
1629                new_voter
1630            );
1631        }
1632        for i in 3 + epoch_offset..6 + epoch_offset {
1633            assert_eq!(
1634                vote_state.get_and_update_authorized_voter(i).unwrap(),
1635                new_voter2
1636            );
1637        }
1638        for i in 6 + epoch_offset..9 + epoch_offset {
1639            assert_eq!(
1640                vote_state.get_and_update_authorized_voter(i).unwrap(),
1641                new_voter3
1642            );
1643        }
1644        for i in 9 + epoch_offset..=10 + epoch_offset {
1645            assert_eq!(
1646                vote_state.get_and_update_authorized_voter(i).unwrap(),
1647                original_voter
1648            );
1649        }
1650    }
1651
1652    #[test]
1653    fn test_authorized_voter_is_locked_within_epoch() {
1654        let original_voter = Pubkey::new_unique();
1655        let mut vote_state = VoteState::new(
1656            &VoteInit {
1657                node_pubkey: original_voter,
1658                authorized_voter: original_voter,
1659                authorized_withdrawer: original_voter,
1660                commission: 0,
1661            },
1662            &Clock::default(),
1663        );
1664
1665        // Test that it's not possible to set a new authorized
1666        // voter within the same epoch, even if none has been
1667        // explicitly set before
1668        let new_voter = Pubkey::new_unique();
1669        assert_eq!(
1670            vote_state.set_new_authorized_voter(&new_voter, 1, 1, |_| Ok(())),
1671            Err(VoteError::TooSoonToReauthorize.into())
1672        );
1673
1674        assert_eq!(vote_state.get_authorized_voter(1), Some(original_voter));
1675
1676        // Set a new authorized voter for a future epoch
1677        assert_eq!(
1678            vote_state.set_new_authorized_voter(&new_voter, 1, 2, |_| Ok(())),
1679            Ok(())
1680        );
1681
1682        // Test that it's not possible to set a new authorized
1683        // voter within the same epoch, even if none has been
1684        // explicitly set before
1685        assert_eq!(
1686            vote_state.set_new_authorized_voter(&original_voter, 3, 3, |_| Ok(())),
1687            Err(VoteError::TooSoonToReauthorize.into())
1688        );
1689
1690        assert_eq!(vote_state.get_authorized_voter(3), Some(new_voter));
1691    }
1692
1693    #[test]
1694    fn test_vote_state_size_of() {
1695        let vote_state = VoteState::get_max_sized_vote_state();
1696        let vote_state = VoteStateVersions::new_current(vote_state);
1697        let size = serialized_size(&vote_state).unwrap();
1698        assert_eq!(VoteState::size_of() as u64, size);
1699    }
1700
1701    #[test]
1702    fn test_vote_state_max_size() {
1703        let mut max_sized_data = vec![0; VoteState::size_of()];
1704        let vote_state = VoteState::get_max_sized_vote_state();
1705        let (start_leader_schedule_epoch, _) = vote_state.authorized_voters.last().unwrap();
1706        let start_current_epoch =
1707            start_leader_schedule_epoch - MAX_LEADER_SCHEDULE_EPOCH_OFFSET + 1;
1708
1709        let mut vote_state = Some(vote_state);
1710        for i in start_current_epoch..start_current_epoch + 2 * MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
1711            vote_state.as_mut().map(|vote_state| {
1712                vote_state.set_new_authorized_voter(
1713                    &Pubkey::new_unique(),
1714                    i,
1715                    i + MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
1716                    |_| Ok(()),
1717                )
1718            });
1719
1720            let versioned = VoteStateVersions::new_current(vote_state.take().unwrap());
1721            VoteState::serialize(&versioned, &mut max_sized_data).unwrap();
1722            vote_state = Some(versioned.convert_to_current());
1723        }
1724    }
1725
1726    #[test]
1727    fn test_default_vote_state_is_uninitialized() {
1728        // The default `VoteState` is stored to de-initialize a zero-balance vote account,
1729        // so must remain such that `VoteStateVersions::is_uninitialized()` returns true
1730        // when called on a `VoteStateVersions` that stores it
1731        assert!(VoteStateVersions::new_current(VoteState::default()).is_uninitialized());
1732    }
1733
1734    #[test]
1735    fn test_is_correct_size_and_initialized() {
1736        // Check all zeroes
1737        let mut vote_account_data = vec![0; VoteStateVersions::vote_state_size_of(true)];
1738        assert!(!VoteStateVersions::is_correct_size_and_initialized(
1739            &vote_account_data
1740        ));
1741
1742        // Check default VoteState
1743        let default_account_state = VoteStateVersions::new_current(VoteState::default());
1744        VoteState::serialize(&default_account_state, &mut vote_account_data).unwrap();
1745        assert!(!VoteStateVersions::is_correct_size_and_initialized(
1746            &vote_account_data
1747        ));
1748
1749        // Check non-zero data shorter than offset index used
1750        let short_data = vec![1; DEFAULT_PRIOR_VOTERS_OFFSET];
1751        assert!(!VoteStateVersions::is_correct_size_and_initialized(
1752            &short_data
1753        ));
1754
1755        // Check non-zero large account
1756        let mut large_vote_data = vec![1; 2 * VoteStateVersions::vote_state_size_of(true)];
1757        let default_account_state = VoteStateVersions::new_current(VoteState::default());
1758        VoteState::serialize(&default_account_state, &mut large_vote_data).unwrap();
1759        assert!(!VoteStateVersions::is_correct_size_and_initialized(
1760            &vote_account_data
1761        ));
1762
1763        // Check populated VoteState
1764        let vote_state = VoteState::new(
1765            &VoteInit {
1766                node_pubkey: Pubkey::new_unique(),
1767                authorized_voter: Pubkey::new_unique(),
1768                authorized_withdrawer: Pubkey::new_unique(),
1769                commission: 0,
1770            },
1771            &Clock::default(),
1772        );
1773        let account_state = VoteStateVersions::new_current(vote_state.clone());
1774        VoteState::serialize(&account_state, &mut vote_account_data).unwrap();
1775        assert!(VoteStateVersions::is_correct_size_and_initialized(
1776            &vote_account_data
1777        ));
1778
1779        // Check old VoteState that hasn't been upgraded to newest version yet
1780        let old_vote_state = VoteState1_14_11::from(vote_state);
1781        let account_state = VoteStateVersions::V1_14_11(Box::new(old_vote_state));
1782        let mut vote_account_data = vec![0; VoteStateVersions::vote_state_size_of(false)];
1783        VoteState::serialize(&account_state, &mut vote_account_data).unwrap();
1784        assert!(VoteStateVersions::is_correct_size_and_initialized(
1785            &vote_account_data
1786        ));
1787    }
1788
1789    #[test]
1790    fn test_minimum_balance() {
1791        let rent = solana_rent::Rent::default();
1792        let minimum_balance = rent.minimum_balance(VoteState::size_of());
1793        // golden, may need updating when vote_state grows
1794        assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)
1795    }
1796
1797    #[test]
1798    fn test_serde_compact_vote_state_update() {
1799        let mut rng = rand::thread_rng();
1800        for _ in 0..5000 {
1801            run_serde_compact_vote_state_update(&mut rng);
1802        }
1803    }
1804
1805    fn run_serde_compact_vote_state_update<R: Rng>(rng: &mut R) {
1806        let lockouts: VecDeque<_> = std::iter::repeat_with(|| {
1807            let slot = 149_303_885_u64.saturating_add(rng.gen_range(0..10_000));
1808            let confirmation_count = rng.gen_range(0..33);
1809            Lockout::new_with_confirmation_count(slot, confirmation_count)
1810        })
1811        .take(32)
1812        .sorted_by_key(|lockout| lockout.slot())
1813        .collect();
1814        let root = rng.gen_ratio(1, 2).then(|| {
1815            lockouts[0]
1816                .slot()
1817                .checked_sub(rng.gen_range(0..1_000))
1818                .expect("All slots should be greater than 1_000")
1819        });
1820        let timestamp = rng.gen_ratio(1, 2).then(|| rng.gen());
1821        let hash = Hash::from(rng.gen::<[u8; 32]>());
1822        let vote_state_update = VoteStateUpdate {
1823            lockouts,
1824            root,
1825            hash,
1826            timestamp,
1827        };
1828        #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
1829        enum VoteInstruction {
1830            #[serde(with = "serde_compact_vote_state_update")]
1831            UpdateVoteState(VoteStateUpdate),
1832            UpdateVoteStateSwitch(
1833                #[serde(with = "serde_compact_vote_state_update")] VoteStateUpdate,
1834                Hash,
1835            ),
1836        }
1837        let vote = VoteInstruction::UpdateVoteState(vote_state_update.clone());
1838        let bytes = bincode::serialize(&vote).unwrap();
1839        assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1840        let hash = Hash::from(rng.gen::<[u8; 32]>());
1841        let vote = VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash);
1842        let bytes = bincode::serialize(&vote).unwrap();
1843        assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1844    }
1845
1846    #[test]
1847    fn test_circbuf_oob() {
1848        // Craft an invalid CircBuf with out-of-bounds index
1849        let data: &[u8] = &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00];
1850        let circ_buf: CircBuf<()> = bincode::deserialize(data).unwrap();
1851        assert_eq!(circ_buf.last(), None);
1852    }
1853}