solana_program_ed25519_dalek_bump/vote/state/
mod.rs

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