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