solana_runtime/
vote_account.rs

1use {
2    itertools::Itertools,
3    once_cell::sync::OnceCell,
4    serde::ser::{Serialize, Serializer},
5    solana_sdk::{
6        account::{AccountSharedData, ReadableAccount},
7        instruction::InstructionError,
8        pubkey::Pubkey,
9    },
10    solana_vote_program::vote_state::VoteState,
11    std::{
12        cmp::Ordering,
13        collections::{hash_map::Entry, HashMap},
14        iter::FromIterator,
15        sync::Arc,
16    },
17    thiserror::Error,
18};
19
20#[derive(Clone, Debug, PartialEq, AbiExample, Deserialize)]
21#[serde(try_from = "AccountSharedData")]
22pub struct VoteAccount(Arc<VoteAccountInner>);
23
24#[derive(Debug, Error)]
25pub enum Error {
26    #[error(transparent)]
27    InstructionError(#[from] InstructionError),
28    #[error("Invalid vote account owner: {0}")]
29    InvalidOwner(/*owner:*/ Pubkey),
30}
31
32#[derive(Debug, AbiExample)]
33struct VoteAccountInner {
34    account: AccountSharedData,
35    vote_state: OnceCell<Result<VoteState, Error>>,
36}
37
38pub type VoteAccountsHashMap = HashMap<Pubkey, (/*stake:*/ u64, VoteAccount)>;
39
40#[derive(Clone, Debug, AbiExample, Deserialize)]
41#[serde(from = "Arc<VoteAccountsHashMap>")]
42pub struct VoteAccounts {
43    vote_accounts: Arc<VoteAccountsHashMap>,
44    // Inner Arc is meant to implement copy-on-write semantics.
45    staked_nodes: OnceCell<
46        Arc<
47            HashMap<
48                Pubkey, // VoteAccount.vote_state.node_pubkey.
49                u64,    // Total stake across all vote-accounts.
50            >,
51        >,
52    >,
53}
54
55impl VoteAccount {
56    pub(crate) fn account(&self) -> &AccountSharedData {
57        &self.0.account
58    }
59
60    pub(crate) fn lamports(&self) -> u64 {
61        self.0.account.lamports()
62    }
63
64    pub(crate) fn owner(&self) -> &Pubkey {
65        self.0.account.owner()
66    }
67
68    pub fn vote_state(&self) -> &Result<VoteState, Error> {
69        // VoteState::deserialize deserializes a VoteStateVersions and then
70        // calls VoteStateVersions::convert_to_current.
71        self.0
72            .vote_state
73            .get_or_init(|| VoteState::deserialize(self.0.account.data()).map_err(Error::from))
74    }
75
76    pub(crate) fn is_deserialized(&self) -> bool {
77        self.0.vote_state.get().is_some()
78    }
79
80    /// VoteState.node_pubkey of this vote-account.
81    pub fn node_pubkey(&self) -> Option<Pubkey> {
82        Some(self.vote_state().as_ref().ok()?.node_pubkey)
83    }
84}
85
86impl VoteAccounts {
87    pub(crate) fn len(&self) -> usize {
88        self.vote_accounts.len()
89    }
90
91    pub fn staked_nodes(&self) -> Arc<HashMap</*node_pubkey:*/ Pubkey, /*stake:*/ u64>> {
92        self.staked_nodes
93            .get_or_init(|| {
94                Arc::new(
95                    self.vote_accounts
96                        .values()
97                        .filter(|(stake, _)| *stake != 0u64)
98                        .filter_map(|(stake, vote_account)| {
99                            Some((vote_account.node_pubkey()?, stake))
100                        })
101                        .into_grouping_map()
102                        .aggregate(|acc, _node_pubkey, stake| {
103                            Some(acc.unwrap_or_default() + stake)
104                        }),
105                )
106            })
107            .clone()
108    }
109
110    pub(crate) fn get(&self, pubkey: &Pubkey) -> Option<&VoteAccount> {
111        let (_stake, vote_account) = self.vote_accounts.get(pubkey)?;
112        Some(vote_account)
113    }
114
115    pub fn get_delegated_stake(&self, pubkey: &Pubkey) -> u64 {
116        self.vote_accounts
117            .get(pubkey)
118            .map(|(stake, _vote_account)| *stake)
119            .unwrap_or_default()
120    }
121
122    pub(crate) fn iter(&self) -> impl Iterator<Item = (&Pubkey, &VoteAccount)> {
123        self.vote_accounts
124            .iter()
125            .map(|(vote_pubkey, (_stake, vote_account))| (vote_pubkey, vote_account))
126    }
127
128    pub(crate) fn delegated_stakes(&self) -> impl Iterator<Item = (&Pubkey, u64)> {
129        self.vote_accounts
130            .iter()
131            .map(|(vote_pubkey, (stake, _vote_account))| (vote_pubkey, *stake))
132    }
133
134    pub(crate) fn find_max_by_delegated_stake(&self) -> Option<&VoteAccount> {
135        let key = |(_pubkey, (stake, _vote_account)): &(_, &(u64, _))| *stake;
136        let (_pubkey, (_stake, vote_account)) = self.vote_accounts.iter().max_by_key(key)?;
137        Some(vote_account)
138    }
139
140    pub(crate) fn insert(&mut self, pubkey: Pubkey, (stake, vote_account): (u64, VoteAccount)) {
141        self.add_node_stake(stake, &vote_account);
142        let vote_accounts = Arc::make_mut(&mut self.vote_accounts);
143        if let Some((stake, vote_account)) = vote_accounts.insert(pubkey, (stake, vote_account)) {
144            self.sub_node_stake(stake, &vote_account);
145        }
146    }
147
148    pub(crate) fn remove(&mut self, pubkey: &Pubkey) -> Option<(u64, VoteAccount)> {
149        let vote_accounts = Arc::make_mut(&mut self.vote_accounts);
150        let entry = vote_accounts.remove(pubkey);
151        if let Some((stake, ref vote_account)) = entry {
152            self.sub_node_stake(stake, vote_account);
153        }
154        entry
155    }
156
157    pub(crate) fn add_stake(&mut self, pubkey: &Pubkey, delta: u64) {
158        let vote_accounts = Arc::make_mut(&mut self.vote_accounts);
159        if let Some((stake, vote_account)) = vote_accounts.get_mut(pubkey) {
160            *stake += delta;
161            let vote_account = vote_account.clone();
162            self.add_node_stake(delta, &vote_account);
163        }
164    }
165
166    pub(crate) fn sub_stake(&mut self, pubkey: &Pubkey, delta: u64) {
167        let vote_accounts = Arc::make_mut(&mut self.vote_accounts);
168        if let Some((stake, vote_account)) = vote_accounts.get_mut(pubkey) {
169            *stake = stake
170                .checked_sub(delta)
171                .expect("subtraction value exceeds account's stake");
172            let vote_account = vote_account.clone();
173            self.sub_node_stake(delta, &vote_account);
174        }
175    }
176
177    fn add_node_stake(&mut self, stake: u64, vote_account: &VoteAccount) {
178        if stake == 0u64 {
179            return;
180        }
181        let staked_nodes = match self.staked_nodes.get_mut() {
182            None => return,
183            Some(staked_nodes) => staked_nodes,
184        };
185        if let Some(node_pubkey) = vote_account.node_pubkey() {
186            Arc::make_mut(staked_nodes)
187                .entry(node_pubkey)
188                .and_modify(|s| *s += stake)
189                .or_insert(stake);
190        }
191    }
192
193    fn sub_node_stake(&mut self, stake: u64, vote_account: &VoteAccount) {
194        if stake == 0u64 {
195            return;
196        }
197        let staked_nodes = match self.staked_nodes.get_mut() {
198            None => return,
199            Some(staked_nodes) => staked_nodes,
200        };
201        if let Some(node_pubkey) = vote_account.node_pubkey() {
202            match Arc::make_mut(staked_nodes).entry(node_pubkey) {
203                Entry::Vacant(_) => panic!("this should not happen!"),
204                Entry::Occupied(mut entry) => match entry.get().cmp(&stake) {
205                    Ordering::Less => panic!("subtraction value exceeds node's stake"),
206                    Ordering::Equal => {
207                        entry.remove_entry();
208                    }
209                    Ordering::Greater => *entry.get_mut() -= stake,
210                },
211            }
212        }
213    }
214}
215
216impl Serialize for VoteAccount {
217    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
218    where
219        S: Serializer,
220    {
221        self.0.account.serialize(serializer)
222    }
223}
224
225impl From<VoteAccount> for AccountSharedData {
226    fn from(account: VoteAccount) -> Self {
227        account.0.account.clone()
228    }
229}
230
231impl TryFrom<AccountSharedData> for VoteAccount {
232    type Error = Error;
233    fn try_from(account: AccountSharedData) -> Result<Self, Self::Error> {
234        let vote_account = VoteAccountInner::try_from(account)?;
235        Ok(Self(Arc::new(vote_account)))
236    }
237}
238
239impl TryFrom<AccountSharedData> for VoteAccountInner {
240    type Error = Error;
241    fn try_from(account: AccountSharedData) -> Result<Self, Self::Error> {
242        if !solana_vote_program::check_id(account.owner()) {
243            return Err(Error::InvalidOwner(*account.owner()));
244        }
245        Ok(Self {
246            account,
247            vote_state: OnceCell::new(),
248        })
249    }
250}
251
252impl PartialEq<VoteAccountInner> for VoteAccountInner {
253    fn eq(&self, other: &Self) -> bool {
254        let Self {
255            account,
256            vote_state: _,
257        } = self;
258        account == &other.account
259    }
260}
261
262impl Default for VoteAccounts {
263    fn default() -> Self {
264        Self {
265            vote_accounts: Arc::default(),
266            staked_nodes: OnceCell::new(),
267        }
268    }
269}
270
271impl PartialEq<VoteAccounts> for VoteAccounts {
272    fn eq(&self, other: &Self) -> bool {
273        let Self {
274            vote_accounts,
275            staked_nodes: _,
276        } = self;
277        vote_accounts == &other.vote_accounts
278    }
279}
280
281impl From<Arc<VoteAccountsHashMap>> for VoteAccounts {
282    fn from(vote_accounts: Arc<VoteAccountsHashMap>) -> Self {
283        Self {
284            vote_accounts,
285            staked_nodes: OnceCell::new(),
286        }
287    }
288}
289
290impl AsRef<VoteAccountsHashMap> for VoteAccounts {
291    fn as_ref(&self) -> &VoteAccountsHashMap {
292        &self.vote_accounts
293    }
294}
295
296impl From<&VoteAccounts> for Arc<VoteAccountsHashMap> {
297    fn from(vote_accounts: &VoteAccounts) -> Self {
298        Arc::clone(&vote_accounts.vote_accounts)
299    }
300}
301
302impl FromIterator<(Pubkey, (/*stake:*/ u64, VoteAccount))> for VoteAccounts {
303    fn from_iter<I>(iter: I) -> Self
304    where
305        I: IntoIterator<Item = (Pubkey, (u64, VoteAccount))>,
306    {
307        Self::from(Arc::new(HashMap::from_iter(iter)))
308    }
309}
310
311impl Serialize for VoteAccounts {
312    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
313    where
314        S: Serializer,
315    {
316        self.vote_accounts.serialize(serializer)
317    }
318}
319
320#[cfg(test)]
321mod tests {
322    use {
323        super::*,
324        bincode::Options,
325        rand::Rng,
326        solana_sdk::{pubkey::Pubkey, sysvar::clock::Clock},
327        solana_vote_program::vote_state::{VoteInit, VoteStateVersions},
328        std::iter::repeat_with,
329    };
330
331    fn new_rand_vote_account<R: Rng>(
332        rng: &mut R,
333        node_pubkey: Option<Pubkey>,
334    ) -> (AccountSharedData, VoteState) {
335        let vote_init = VoteInit {
336            node_pubkey: node_pubkey.unwrap_or_else(Pubkey::new_unique),
337            authorized_voter: Pubkey::new_unique(),
338            authorized_withdrawer: Pubkey::new_unique(),
339            commission: rng.gen(),
340        };
341        let clock = Clock {
342            slot: rng.gen(),
343            epoch_start_timestamp: rng.gen(),
344            epoch: rng.gen(),
345            leader_schedule_epoch: rng.gen(),
346            unix_timestamp: rng.gen(),
347        };
348        let vote_state = VoteState::new(&vote_init, &clock);
349        let account = AccountSharedData::new_data(
350            rng.gen(), // lamports
351            &VoteStateVersions::new_current(vote_state.clone()),
352            &solana_vote_program::id(), // owner
353        )
354        .unwrap();
355        (account, vote_state)
356    }
357
358    fn new_rand_vote_accounts<R: Rng>(
359        rng: &mut R,
360        num_nodes: usize,
361    ) -> impl Iterator<Item = (Pubkey, (/*stake:*/ u64, VoteAccount))> + '_ {
362        let nodes: Vec<_> = repeat_with(Pubkey::new_unique).take(num_nodes).collect();
363        repeat_with(move || {
364            let node = nodes[rng.gen_range(0, nodes.len())];
365            let (account, _) = new_rand_vote_account(rng, Some(node));
366            let stake = rng.gen_range(0, 997);
367            let vote_account = VoteAccount::try_from(account).unwrap();
368            (Pubkey::new_unique(), (stake, vote_account))
369        })
370    }
371
372    fn staked_nodes<'a, I>(vote_accounts: I) -> HashMap<Pubkey, u64>
373    where
374        I: IntoIterator<Item = &'a (Pubkey, (u64, VoteAccount))>,
375    {
376        let mut staked_nodes = HashMap::new();
377        for (_, (stake, vote_account)) in vote_accounts
378            .into_iter()
379            .filter(|(_, (stake, _))| *stake != 0)
380        {
381            if let Some(node_pubkey) = vote_account.node_pubkey() {
382                staked_nodes
383                    .entry(node_pubkey)
384                    .and_modify(|s| *s += *stake)
385                    .or_insert(*stake);
386            }
387        }
388        staked_nodes
389    }
390
391    #[test]
392    fn test_vote_account() {
393        let mut rng = rand::thread_rng();
394        let (account, vote_state) = new_rand_vote_account(&mut rng, None);
395        let lamports = account.lamports();
396        let vote_account = VoteAccount::try_from(account).unwrap();
397        assert_eq!(lamports, vote_account.lamports());
398        assert_eq!(vote_state, *vote_account.vote_state().as_ref().unwrap());
399        // 2nd call to .vote_state() should return the cached value.
400        assert_eq!(vote_state, *vote_account.vote_state().as_ref().unwrap());
401    }
402
403    #[test]
404    fn test_vote_account_serialize() {
405        let mut rng = rand::thread_rng();
406        let (account, vote_state) = new_rand_vote_account(&mut rng, None);
407        let vote_account = VoteAccount::try_from(account.clone()).unwrap();
408        assert_eq!(vote_state, *vote_account.vote_state().as_ref().unwrap());
409        // Assert than VoteAccount has the same wire format as Account.
410        assert_eq!(
411            bincode::serialize(&account).unwrap(),
412            bincode::serialize(&vote_account).unwrap()
413        );
414    }
415
416    #[test]
417    fn test_vote_account_deserialize() {
418        let mut rng = rand::thread_rng();
419        let (account, vote_state) = new_rand_vote_account(&mut rng, None);
420        let data = bincode::serialize(&account).unwrap();
421        let vote_account = VoteAccount::try_from(account).unwrap();
422        assert_eq!(vote_state, *vote_account.vote_state().as_ref().unwrap());
423        let other_vote_account: VoteAccount = bincode::deserialize(&data).unwrap();
424        assert_eq!(vote_account, other_vote_account);
425        assert_eq!(
426            vote_state,
427            *other_vote_account.vote_state().as_ref().unwrap()
428        );
429    }
430
431    #[test]
432    fn test_vote_account_round_trip() {
433        let mut rng = rand::thread_rng();
434        let (account, vote_state) = new_rand_vote_account(&mut rng, None);
435        let vote_account = VoteAccount::try_from(account).unwrap();
436        assert_eq!(vote_state, *vote_account.vote_state().as_ref().unwrap());
437        let data = bincode::serialize(&vote_account).unwrap();
438        let other_vote_account: VoteAccount = bincode::deserialize(&data).unwrap();
439        // Assert that serialize->deserialized returns the same VoteAccount.
440        assert_eq!(vote_account, other_vote_account);
441        assert_eq!(
442            vote_state,
443            *other_vote_account.vote_state().as_ref().unwrap()
444        );
445    }
446
447    #[test]
448    fn test_vote_accounts_serialize() {
449        let mut rng = rand::thread_rng();
450        let vote_accounts_hash_map: VoteAccountsHashMap =
451            new_rand_vote_accounts(&mut rng, 64).take(1024).collect();
452        let vote_accounts = VoteAccounts::from(Arc::new(vote_accounts_hash_map.clone()));
453        assert!(vote_accounts.staked_nodes().len() > 32);
454        assert_eq!(
455            bincode::serialize(&vote_accounts).unwrap(),
456            bincode::serialize(&vote_accounts_hash_map).unwrap(),
457        );
458        assert_eq!(
459            bincode::options().serialize(&vote_accounts).unwrap(),
460            bincode::options()
461                .serialize(&vote_accounts_hash_map)
462                .unwrap(),
463        )
464    }
465
466    #[test]
467    fn test_vote_accounts_deserialize() {
468        let mut rng = rand::thread_rng();
469        let vote_accounts_hash_map: VoteAccountsHashMap =
470            new_rand_vote_accounts(&mut rng, 64).take(1024).collect();
471        let data = bincode::serialize(&vote_accounts_hash_map).unwrap();
472        let vote_accounts: VoteAccounts = bincode::deserialize(&data).unwrap();
473        assert!(vote_accounts.staked_nodes().len() > 32);
474        assert_eq!(*vote_accounts.vote_accounts, vote_accounts_hash_map);
475        let data = bincode::options()
476            .serialize(&vote_accounts_hash_map)
477            .unwrap();
478        let vote_accounts: VoteAccounts = bincode::options().deserialize(&data).unwrap();
479        assert_eq!(*vote_accounts.vote_accounts, vote_accounts_hash_map);
480    }
481
482    #[test]
483    fn test_staked_nodes() {
484        let mut rng = rand::thread_rng();
485        let mut accounts: Vec<_> = new_rand_vote_accounts(&mut rng, 64).take(1024).collect();
486        let mut vote_accounts = VoteAccounts::default();
487        // Add vote accounts.
488        for (k, (pubkey, (stake, vote_account))) in accounts.iter().enumerate() {
489            vote_accounts.insert(*pubkey, (*stake, vote_account.clone()));
490            if (k + 1) % 128 == 0 {
491                assert_eq!(
492                    staked_nodes(&accounts[..k + 1]),
493                    *vote_accounts.staked_nodes()
494                );
495            }
496        }
497        // Remove some of the vote accounts.
498        for k in 0..256 {
499            let index = rng.gen_range(0, accounts.len());
500            let (pubkey, (_, _)) = accounts.swap_remove(index);
501            vote_accounts.remove(&pubkey);
502            if (k + 1) % 32 == 0 {
503                assert_eq!(staked_nodes(&accounts), *vote_accounts.staked_nodes());
504            }
505        }
506        // Modify the stakes for some of the accounts.
507        for k in 0..2048 {
508            let index = rng.gen_range(0, accounts.len());
509            let (pubkey, (stake, _)) = &mut accounts[index];
510            let new_stake = rng.gen_range(0, 997);
511            if new_stake < *stake {
512                vote_accounts.sub_stake(pubkey, *stake - new_stake);
513            } else {
514                vote_accounts.add_stake(pubkey, new_stake - *stake);
515            }
516            *stake = new_stake;
517            if (k + 1) % 128 == 0 {
518                assert_eq!(staked_nodes(&accounts), *vote_accounts.staked_nodes());
519            }
520        }
521        // Remove everything.
522        while !accounts.is_empty() {
523            let index = rng.gen_range(0, accounts.len());
524            let (pubkey, (_, _)) = accounts.swap_remove(index);
525            vote_accounts.remove(&pubkey);
526            if accounts.len() % 32 == 0 {
527                assert_eq!(staked_nodes(&accounts), *vote_accounts.staked_nodes());
528            }
529        }
530        assert!(vote_accounts.staked_nodes.get().unwrap().is_empty());
531    }
532
533    // Asserts that returned staked-nodes are copy-on-write references.
534    #[test]
535    fn test_staked_nodes_cow() {
536        let mut rng = rand::thread_rng();
537        let mut accounts = new_rand_vote_accounts(&mut rng, 64);
538        // Add vote accounts.
539        let mut vote_accounts = VoteAccounts::default();
540        for (pubkey, (stake, vote_account)) in (&mut accounts).take(1024) {
541            vote_accounts.insert(pubkey, (stake, vote_account));
542        }
543        let staked_nodes = vote_accounts.staked_nodes();
544        let (pubkey, (more_stake, vote_account)) =
545            accounts.find(|(_, (stake, _))| *stake != 0).unwrap();
546        let node_pubkey = vote_account.node_pubkey().unwrap();
547        vote_accounts.insert(pubkey, (more_stake, vote_account));
548        assert_ne!(staked_nodes, vote_accounts.staked_nodes());
549        assert_eq!(
550            vote_accounts.staked_nodes()[&node_pubkey],
551            more_stake + staked_nodes.get(&node_pubkey).copied().unwrap_or_default()
552        );
553        for (pubkey, stake) in vote_accounts.staked_nodes().iter() {
554            if *pubkey != node_pubkey {
555                assert_eq!(*stake, staked_nodes[pubkey]);
556            } else {
557                assert_eq!(
558                    *stake,
559                    more_stake + staked_nodes.get(pubkey).copied().unwrap_or_default()
560                );
561            }
562        }
563    }
564
565    // Asserts that returned vote-accounts are copy-on-write references.
566    #[test]
567    fn test_vote_accounts_cow() {
568        let mut rng = rand::thread_rng();
569        let mut accounts = new_rand_vote_accounts(&mut rng, 64);
570        // Add vote accounts.
571        let mut vote_accounts = VoteAccounts::default();
572        for (pubkey, (stake, vote_account)) in (&mut accounts).take(1024) {
573            vote_accounts.insert(pubkey, (stake, vote_account));
574        }
575        let vote_accounts_hashmap = Arc::<VoteAccountsHashMap>::from(&vote_accounts);
576        assert_eq!(vote_accounts_hashmap, vote_accounts.vote_accounts);
577        assert!(Arc::ptr_eq(
578            &vote_accounts_hashmap,
579            &vote_accounts.vote_accounts
580        ));
581        let (pubkey, (more_stake, vote_account)) =
582            accounts.find(|(_, (stake, _))| *stake != 0).unwrap();
583        vote_accounts.insert(pubkey, (more_stake, vote_account.clone()));
584        assert!(!Arc::ptr_eq(
585            &vote_accounts_hashmap,
586            &vote_accounts.vote_accounts
587        ));
588        assert_ne!(vote_accounts_hashmap, vote_accounts.vote_accounts);
589        let other = (more_stake, vote_account);
590        for (pk, value) in vote_accounts.vote_accounts.iter() {
591            if *pk != pubkey {
592                assert_eq!(value, &vote_accounts_hashmap[pk]);
593            } else {
594                assert_eq!(value, &other);
595            }
596        }
597    }
598}