waffles_solana_program/vote/state/
mod.rs

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