solana_vote_interface/state/
vote_state_1_14_11.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use super::*;
#[cfg(test)]
use arbitrary::Arbitrary;

// Offset used for VoteState version 1_14_11
const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 82;

#[cfg_attr(
    feature = "frozen-abi",
    solana_frozen_abi_macro::frozen_abi(digest = "HF4NfshaLg9e93RURYWTJRowtRrpLf5mWiF4G2Gnfu2r"),
    derive(solana_frozen_abi_macro::AbiExample)
)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, Default, PartialEq, Eq, Clone)]
#[cfg_attr(test, derive(Arbitrary))]
pub struct VoteState1_14_11 {
    /// the node that votes in this account
    pub node_pubkey: Pubkey,

    /// the signer for withdrawals
    pub authorized_withdrawer: Pubkey,
    /// percentage (0-100) that represents what part of a rewards
    ///  payout should be given to this VoteAccount
    pub commission: u8,

    pub votes: VecDeque<Lockout>,

    // This usually the last Lockout which was popped from self.votes.
    // However, it can be arbitrary slot, when being used inside Tower
    pub root_slot: Option<Slot>,

    /// the signer for vote transactions
    pub authorized_voters: AuthorizedVoters,

    /// history of prior authorized voters and the epochs for which
    /// they were set, the bottom end of the range is inclusive,
    /// the top of the range is exclusive
    pub prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>,

    /// history of how many credits earned by the end of each epoch
    ///  each tuple is (Epoch, credits, prev_credits)
    pub epoch_credits: Vec<(Epoch, u64, u64)>,

    /// most recent timestamp submitted with a vote
    pub last_timestamp: BlockTimestamp,
}

impl VoteState1_14_11 {
    pub fn get_rent_exempt_reserve(rent: &Rent) -> u64 {
        rent.minimum_balance(Self::size_of())
    }

    /// Upper limit on the size of the Vote State
    /// when votes.len() is MAX_LOCKOUT_HISTORY.
    pub fn size_of() -> usize {
        3731 // see test_vote_state_size_of
    }

    pub fn is_correct_size_and_initialized(data: &[u8]) -> bool {
        const VERSION_OFFSET: usize = 4;
        const DEFAULT_PRIOR_VOTERS_END: usize = VERSION_OFFSET + DEFAULT_PRIOR_VOTERS_OFFSET;
        data.len() == VoteState1_14_11::size_of()
            && data[VERSION_OFFSET..DEFAULT_PRIOR_VOTERS_END] != [0; DEFAULT_PRIOR_VOTERS_OFFSET]
    }
}

impl From<VoteState> for VoteState1_14_11 {
    fn from(vote_state: VoteState) -> Self {
        Self {
            node_pubkey: vote_state.node_pubkey,
            authorized_withdrawer: vote_state.authorized_withdrawer,
            commission: vote_state.commission,
            votes: vote_state
                .votes
                .into_iter()
                .map(|landed_vote| landed_vote.into())
                .collect(),
            root_slot: vote_state.root_slot,
            authorized_voters: vote_state.authorized_voters,
            prior_voters: vote_state.prior_voters,
            epoch_credits: vote_state.epoch_credits,
            last_timestamp: vote_state.last_timestamp,
        }
    }
}

#[cfg(test)]
mod tests {
    use {super::*, core::mem::MaybeUninit};

    #[test]
    fn test_vote_deserialize_1_14_11() {
        // base case
        let target_vote_state = VoteState1_14_11::default();
        let target_vote_state_versions = VoteStateVersions::V1_14_11(Box::new(target_vote_state));
        let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();

        let mut test_vote_state = MaybeUninit::uninit();
        VoteState::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state).unwrap();
        let test_vote_state = unsafe { test_vote_state.assume_init() };

        assert_eq!(
            target_vote_state_versions.convert_to_current(),
            test_vote_state
        );

        // variant
        // provide 4x the minimum struct size in bytes to ensure we typically touch every field
        let struct_bytes_x4 = std::mem::size_of::<VoteState1_14_11>() * 4;
        for _ in 0..1000 {
            let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
            let mut unstructured = Unstructured::new(&raw_data);

            let arbitrary_vote_state = VoteState1_14_11::arbitrary(&mut unstructured).unwrap();
            let target_vote_state_versions =
                VoteStateVersions::V1_14_11(Box::new(arbitrary_vote_state));

            let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
            let target_vote_state = target_vote_state_versions.convert_to_current();

            let mut test_vote_state = MaybeUninit::uninit();
            VoteState::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state).unwrap();
            let test_vote_state = unsafe { test_vote_state.assume_init() };

            assert_eq!(target_vote_state, test_vote_state);
        }
    }
}