1use {
2 crate::program::vote::{authorized_voters::AuthorizedVoters, id, vote_error::VoteError},
3 bincode::{deserialize, serialize_into, ErrorKind},
4 serde_derive::{Deserialize, Serialize},
5 solana_sdk::{
6 account::{AccountSharedData, ReadableAccount, WritableAccount},
7 clock::{Epoch, Slot, UnixTimestamp},
8 feature_set::{self, filter_votes_outside_slot_hashes, FeatureSet},
9 hash::Hash,
10 instruction::InstructionError,
11 pubkey::Pubkey,
12 rent::Rent,
13 slot_hashes::SlotHash,
14 sysvar::clock::Clock,
15 transaction_context::{BorrowedAccount, InstructionContext, TransactionContext},
16 },
17 std::{
18 cmp::Ordering,
19 collections::{HashSet, VecDeque},
20 fmt::Debug,
21 },
22};
23
24mod vote_state_0_23_5;
25pub mod vote_state_versions;
26pub use vote_state_versions::*;
27
28pub const MAX_LOCKOUT_HISTORY: usize = 31;
30pub const INITIAL_LOCKOUT: usize = 2;
31
32pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
34
35const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 82;
37
38#[frozen_abi(digest = "6LBwH5w3WyAWZhsM3KTG9QZP7nYBhcC61K33kHR6gMAD")]
39#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, AbiEnumVisitor, AbiExample)]
40pub enum VoteTransaction {
41 Vote(Vote),
42 VoteStateUpdate(VoteStateUpdate),
43}
44
45impl VoteTransaction {
46 pub fn slots(&self) -> Vec<Slot> {
47 match self {
48 VoteTransaction::Vote(vote) => vote.slots.clone(),
49 VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.slots(),
50 }
51 }
52
53 pub fn slot(&self, i: usize) -> Slot {
54 match self {
55 VoteTransaction::Vote(vote) => vote.slots[i],
56 VoteTransaction::VoteStateUpdate(vote_state_update) => {
57 vote_state_update.lockouts[i].slot
58 }
59 }
60 }
61
62 pub fn len(&self) -> usize {
63 match self {
64 VoteTransaction::Vote(vote) => vote.slots.len(),
65 VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.lockouts.len(),
66 }
67 }
68
69 pub fn is_empty(&self) -> bool {
70 match self {
71 VoteTransaction::Vote(vote) => vote.slots.is_empty(),
72 VoteTransaction::VoteStateUpdate(vote_state_update) => {
73 vote_state_update.lockouts.is_empty()
74 }
75 }
76 }
77
78 pub fn hash(&self) -> Hash {
79 match self {
80 VoteTransaction::Vote(vote) => vote.hash,
81 VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.hash,
82 }
83 }
84
85 pub fn timestamp(&self) -> Option<UnixTimestamp> {
86 match self {
87 VoteTransaction::Vote(vote) => vote.timestamp,
88 VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.timestamp,
89 }
90 }
91
92 pub fn set_timestamp(&mut self, ts: Option<UnixTimestamp>) {
93 match self {
94 VoteTransaction::Vote(vote) => vote.timestamp = ts,
95 VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.timestamp = ts,
96 }
97 }
98
99 pub fn last_voted_slot(&self) -> Option<Slot> {
100 match self {
101 VoteTransaction::Vote(vote) => vote.slots.last().copied(),
102 VoteTransaction::VoteStateUpdate(vote_state_update) => {
103 Some(vote_state_update.lockouts.back()?.slot)
104 }
105 }
106 }
107
108 pub fn last_voted_slot_hash(&self) -> Option<(Slot, Hash)> {
109 Some((self.last_voted_slot()?, self.hash()))
110 }
111}
112
113impl From<Vote> for VoteTransaction {
114 fn from(vote: Vote) -> Self {
115 VoteTransaction::Vote(vote)
116 }
117}
118
119impl From<VoteStateUpdate> for VoteTransaction {
120 fn from(vote_state_update: VoteStateUpdate) -> Self {
121 VoteTransaction::VoteStateUpdate(vote_state_update)
122 }
123}
124
125#[frozen_abi(digest = "Ch2vVEwos2EjAVqSHCyJjnN2MNX1yrpapZTGhMSCjWUH")]
126#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
127pub struct Vote {
128 pub slots: Vec<Slot>,
130 pub hash: Hash,
132 pub timestamp: Option<UnixTimestamp>,
134}
135
136impl Vote {
137 pub fn new(slots: Vec<Slot>, hash: Hash) -> Self {
138 Self {
139 slots,
140 hash,
141 timestamp: None,
142 }
143 }
144}
145
146#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Copy, Clone, AbiExample)]
147pub struct Lockout {
148 pub slot: Slot,
149 pub confirmation_count: u32,
150}
151
152impl Lockout {
153 pub fn new(slot: Slot) -> Self {
154 Self {
155 slot,
156 confirmation_count: 1,
157 }
158 }
159
160 pub fn lockout(&self) -> u64 {
162 (INITIAL_LOCKOUT as u64).pow(self.confirmation_count)
163 }
164
165 pub fn last_locked_out_slot(&self) -> Slot {
169 self.slot + self.lockout()
170 }
171
172 pub fn is_locked_out_at_slot(&self, slot: Slot) -> bool {
173 self.last_locked_out_slot() >= slot
174 }
175}
176
177#[frozen_abi(digest = "BctadFJjUKbvPJzr6TszbX6rBfQUNSRKpKKngkzgXgeY")]
178#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
179pub struct VoteStateUpdate {
180 pub lockouts: VecDeque<Lockout>,
182 pub root: Option<Slot>,
184 pub hash: Hash,
186 pub timestamp: Option<UnixTimestamp>,
188}
189
190impl From<Vec<(Slot, u32)>> for VoteStateUpdate {
191 fn from(recent_slots: Vec<(Slot, u32)>) -> Self {
192 let lockouts: VecDeque<Lockout> = recent_slots
193 .into_iter()
194 .map(|(slot, confirmation_count)| Lockout {
195 slot,
196 confirmation_count,
197 })
198 .collect();
199 Self {
200 lockouts,
201 root: None,
202 hash: Hash::default(),
203 timestamp: None,
204 }
205 }
206}
207
208impl VoteStateUpdate {
209 pub fn new(lockouts: VecDeque<Lockout>, root: Option<Slot>, hash: Hash) -> Self {
210 Self {
211 lockouts,
212 root,
213 hash,
214 timestamp: None,
215 }
216 }
217
218 pub fn slots(&self) -> Vec<Slot> {
219 self.lockouts.iter().map(|lockout| lockout.slot).collect()
220 }
221}
222
223#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
224pub struct VoteInit {
225 pub node_pubkey: Pubkey,
226 pub authorized_voter: Pubkey,
227 pub authorized_withdrawer: Pubkey,
228 pub commission: u8,
229}
230
231#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
232pub enum VoteAuthorize {
233 Voter,
234 Withdrawer,
235}
236
237#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
238pub struct BlockTimestamp {
239 pub slot: Slot,
240 pub timestamp: UnixTimestamp,
241}
242
243const MAX_ITEMS: usize = 32;
245
246#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
247pub struct CircBuf<I> {
248 buf: [I; MAX_ITEMS],
249 idx: usize,
251 is_empty: bool,
252}
253
254impl<I: Default + Copy> Default for CircBuf<I> {
255 fn default() -> Self {
256 Self {
257 buf: [I::default(); MAX_ITEMS],
258 idx: MAX_ITEMS - 1,
259 is_empty: true,
260 }
261 }
262}
263
264impl<I> CircBuf<I> {
265 pub fn append(&mut self, item: I) {
266 self.idx += 1;
268 self.idx %= MAX_ITEMS;
269
270 self.buf[self.idx] = item;
271 self.is_empty = false;
272 }
273
274 pub fn buf(&self) -> &[I; MAX_ITEMS] {
275 &self.buf
276 }
277
278 pub fn last(&self) -> Option<&I> {
279 if !self.is_empty {
280 Some(&self.buf[self.idx])
281 } else {
282 None
283 }
284 }
285}
286
287#[frozen_abi(digest = "331ZmXrmsUcwbKhzR3C1UEU6uNwZr48ExE54JDKGWA4w")]
288#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
289pub struct VoteState {
290 pub node_pubkey: Pubkey,
292
293 pub authorized_withdrawer: Pubkey,
295 pub commission: u8,
298
299 pub votes: VecDeque<Lockout>,
300
301 pub root_slot: Option<Slot>,
304
305 authorized_voters: AuthorizedVoters,
307
308 prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>,
312
313 pub epoch_credits: Vec<(Epoch, u64, u64)>,
316
317 pub last_timestamp: BlockTimestamp,
319}
320
321impl VoteState {
322 pub fn new(vote_init: &VoteInit, clock: &Clock) -> Self {
323 Self {
324 node_pubkey: vote_init.node_pubkey,
325 authorized_voters: AuthorizedVoters::new(clock.epoch, vote_init.authorized_voter),
326 authorized_withdrawer: vote_init.authorized_withdrawer,
327 commission: vote_init.commission,
328 ..VoteState::default()
329 }
330 }
331
332 pub fn get_authorized_voter(&self, epoch: Epoch) -> Option<Pubkey> {
333 self.authorized_voters.get_authorized_voter(epoch)
334 }
335
336 pub fn authorized_voters(&self) -> &AuthorizedVoters {
337 &self.authorized_voters
338 }
339
340 pub fn prior_voters(&mut self) -> &CircBuf<(Pubkey, Epoch, Epoch)> {
341 &self.prior_voters
342 }
343
344 pub fn get_rent_exempt_reserve(rent: &Rent) -> u64 {
345 rent.minimum_balance(VoteState::size_of())
346 }
347
348 pub const fn size_of() -> usize {
351 3731 }
353
354 pub fn from<T: ReadableAccount>(account: &T) -> Option<VoteState> {
356 Self::deserialize(account.data()).ok()
357 }
358
359 pub fn to<T: WritableAccount>(versioned: &VoteStateVersions, account: &mut T) -> Option<()> {
361 Self::serialize(versioned, account.data_as_mut_slice()).ok()
362 }
363
364 pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
365 deserialize::<VoteStateVersions>(input)
366 .map(|versioned| versioned.convert_to_current())
367 .map_err(|_| InstructionError::InvalidAccountData)
368 }
369
370 pub fn serialize(
371 versioned: &VoteStateVersions,
372 output: &mut [u8],
373 ) -> Result<(), InstructionError> {
374 serialize_into(output, versioned).map_err(|err| match *err {
375 ErrorKind::SizeLimit => InstructionError::AccountDataTooSmall,
376 _ => InstructionError::GenericError,
377 })
378 }
379
380 pub fn credits_from<T: ReadableAccount>(account: &T) -> Option<u64> {
381 Self::from(account).map(|state| state.credits())
382 }
383
384 pub fn commission_split(&self, on: u64) -> (u64, u64, bool) {
389 match self.commission.min(100) {
390 0 => (0, on, false),
391 100 => (on, 0, false),
392 split => {
393 let on = u128::from(on);
394 let mine = on * u128::from(split) / 100u128;
400 let theirs = on * u128::from(100 - split) / 100u128;
401
402 (mine as u64, theirs as u64, true)
403 }
404 }
405 }
406
407 pub fn contains_slot(&self, candidate_slot: Slot) -> bool {
409 self.votes
410 .binary_search_by(|lockout| lockout.slot.cmp(&candidate_slot))
411 .is_ok()
412 }
413
414 fn check_update_vote_state_slots_are_valid(
415 &self,
416 vote_state_update: &mut VoteStateUpdate,
417 slot_hashes: &[(Slot, Hash)],
418 ) -> Result<(), VoteError> {
419 if vote_state_update.lockouts.is_empty() {
420 return Err(VoteError::EmptySlots);
421 }
422
423 if let Some(last_vote_slot) = self.votes.back().map(|lockout| lockout.slot) {
425 if vote_state_update.lockouts.back().unwrap().slot <= last_vote_slot {
426 return Err(VoteError::VoteTooOld);
427 }
428 }
429
430 let last_vote_state_update_slot = vote_state_update
431 .lockouts
432 .back()
433 .expect("must be nonempty, checked above")
434 .slot;
435
436 if slot_hashes.is_empty() {
437 return Err(VoteError::SlotsMismatch);
438 }
439 let earliest_slot_hash_in_history = slot_hashes.last().unwrap().0;
440
441 if last_vote_state_update_slot < earliest_slot_hash_in_history {
443 return Err(VoteError::VoteTooOld);
446 }
447
448 if let Some(new_proposed_root) = vote_state_update.root {
450 if earliest_slot_hash_in_history > new_proposed_root {
454 vote_state_update.root = self.root_slot;
455 }
456 }
457
458 let mut check_root = vote_state_update.root;
461 let mut vote_state_update_index = 0;
462
463 let mut slot_hashes_index = slot_hashes.len();
466
467 let mut vote_state_update_indexes_to_filter = vec![];
468
469 while vote_state_update_index < vote_state_update.lockouts.len() && slot_hashes_index > 0 {
482 let proposed_vote_slot = if let Some(root) = check_root {
483 root
484 } else {
485 vote_state_update.lockouts[vote_state_update_index].slot
486 };
487 if check_root.is_none()
488 && vote_state_update_index > 0
489 && proposed_vote_slot
490 <= vote_state_update.lockouts[vote_state_update_index - 1].slot
491 {
492 return Err(VoteError::SlotsNotOrdered);
493 }
494 let ancestor_slot = slot_hashes[slot_hashes_index - 1].0;
495
496 match proposed_vote_slot.cmp(&ancestor_slot) {
499 Ordering::Less => {
500 if slot_hashes_index == slot_hashes.len() {
501 assert!(proposed_vote_slot < earliest_slot_hash_in_history);
504 if !self.contains_slot(proposed_vote_slot) && check_root.is_none() {
505 vote_state_update_indexes_to_filter.push(vote_state_update_index);
511 }
512 if check_root.is_some() {
513 assert!(self.root_slot.unwrap() < earliest_slot_hash_in_history);
517 check_root = None;
518 } else {
519 vote_state_update_index += 1;
520 }
521 continue;
522 } else {
523 if check_root.is_some() {
527 return Err(VoteError::RootOnDifferentFork);
528 } else {
529 return Err(VoteError::SlotsMismatch);
530 }
531 }
532 }
533 Ordering::Greater => {
534 slot_hashes_index -= 1;
536 continue;
537 }
538 Ordering::Equal => {
539 if check_root.is_some() {
543 check_root = None;
544 } else {
545 vote_state_update_index += 1;
546 slot_hashes_index -= 1;
547 }
548 }
549 }
550 }
551
552 if vote_state_update_index != vote_state_update.lockouts.len() {
553 return Err(VoteError::SlotsMismatch);
555 }
556
557 assert_eq!(
580 last_vote_state_update_slot,
581 slot_hashes[slot_hashes_index].0
582 );
583
584 if slot_hashes[slot_hashes_index].1 != vote_state_update.hash {
585 return Err(VoteError::SlotHashMismatch);
598 }
599
600 let mut vote_state_update_index = 0;
602 let mut filter_votes_index = 0;
603 vote_state_update.lockouts.retain(|_lockout| {
604 let should_retain = if filter_votes_index == vote_state_update_indexes_to_filter.len() {
605 true
606 } else if vote_state_update_index
607 == vote_state_update_indexes_to_filter[filter_votes_index]
608 {
609 filter_votes_index += 1;
610 false
611 } else {
612 true
613 };
614
615 vote_state_update_index += 1;
616 should_retain
617 });
618
619 Ok(())
620 }
621
622 fn check_slots_are_valid(
623 &self,
624 vote_slots: &[Slot],
625 vote_hash: &Hash,
626 slot_hashes: &[(Slot, Hash)],
627 ) -> Result<(), VoteError> {
628 let mut i = 0;
631
632 let mut j = slot_hashes.len();
635
636 while i < vote_slots.len() && j > 0 {
645 if self
648 .last_voted_slot()
649 .map_or(false, |last_voted_slot| vote_slots[i] <= last_voted_slot)
650 {
651 i += 1;
652 continue;
653 }
654
655 if vote_slots[i] != slot_hashes[j - 1].0 {
657 j -= 1;
659 continue;
660 }
661
662 i += 1;
665 j -= 1;
666 }
667
668 if j == slot_hashes.len() {
669 return Err(VoteError::VoteTooOld);
678 }
679 if i != vote_slots.len() {
680 return Err(VoteError::SlotsMismatch);
689 }
690 if &slot_hashes[j].1 != vote_hash {
691 return Err(VoteError::SlotHashMismatch);
701 }
702 Ok(())
703 }
704
705 pub fn process_new_vote_state(
743 &mut self,
744 new_state: VecDeque<Lockout>,
745 new_root: Option<Slot>,
746 timestamp: Option<i64>,
747 epoch: Epoch,
748 feature_set: Option<&FeatureSet>,
749 ) -> Result<(), VoteError> {
750 assert!(!new_state.is_empty());
751 if new_state.len() > MAX_LOCKOUT_HISTORY {
752 return Err(VoteError::TooManyVotes);
753 }
754
755 match (new_root, self.root_slot) {
756 (Some(new_root), Some(current_root)) => {
757 if new_root < current_root {
758 return Err(VoteError::RootRollBack);
759 }
760 }
761 (None, Some(_)) => {
762 return Err(VoteError::RootRollBack);
763 }
764 _ => (),
765 }
766
767 let mut previous_vote: Option<&Lockout> = None;
768
769 for vote in &new_state {
774 if vote.confirmation_count == 0 {
775 return Err(VoteError::ZeroConfirmations);
776 } else if vote.confirmation_count > MAX_LOCKOUT_HISTORY as u32 {
777 return Err(VoteError::ConfirmationTooLarge);
778 } else if let Some(new_root) = new_root {
779 if vote.slot <= new_root
780 &&
781 new_root != Slot::default()
786 {
787 return Err(VoteError::SlotSmallerThanRoot);
788 }
789 }
790
791 if let Some(previous_vote) = previous_vote {
792 if previous_vote.slot >= vote.slot {
793 return Err(VoteError::SlotsNotOrdered);
794 } else if previous_vote.confirmation_count <= vote.confirmation_count {
795 return Err(VoteError::ConfirmationsNotOrdered);
796 } else if vote.slot > previous_vote.last_locked_out_slot() {
797 return Err(VoteError::NewVoteStateLockoutMismatch);
798 }
799 }
800 previous_vote = Some(vote);
801 }
802
803 let mut current_vote_state_index = 0;
806 let mut new_vote_state_index = 0;
807
808 let mut finalized_slot_count = 1_u64;
815
816 for current_vote in &self.votes {
817 if let Some(new_root) = new_root {
820 if current_vote.slot <= new_root {
821 current_vote_state_index += 1;
822 if current_vote.slot != new_root {
823 finalized_slot_count += 1;
824 }
825 continue;
826 }
827 }
828
829 break;
830 }
831
832 while current_vote_state_index < self.votes.len() && new_vote_state_index < new_state.len()
835 {
836 let current_vote = &self.votes[current_vote_state_index];
837 let new_vote = &new_state[new_vote_state_index];
838
839 match current_vote.slot.cmp(&new_vote.slot) {
843 Ordering::Less => {
844 if current_vote.last_locked_out_slot() >= new_vote.slot {
845 return Err(VoteError::LockoutConflict);
846 }
847 current_vote_state_index += 1;
848 }
849 Ordering::Equal => {
850 if new_vote.confirmation_count < current_vote.confirmation_count {
853 return Err(VoteError::ConfirmationRollBack);
854 }
855
856 current_vote_state_index += 1;
857 new_vote_state_index += 1;
858 }
859 Ordering::Greater => {
860 new_vote_state_index += 1;
861 }
862 }
863 }
864
865 if self.root_slot != new_root {
868 if feature_set
870 .map(|feature_set| {
871 feature_set.is_active(&feature_set::vote_state_update_credit_per_dequeue::id())
872 })
873 .unwrap_or(false)
874 {
875 self.increment_credits(epoch, finalized_slot_count);
878 } else {
879 self.increment_credits(epoch, 1);
880 }
881 }
882 if let Some(timestamp) = timestamp {
883 let last_slot = new_state.back().unwrap().slot;
884 self.process_timestamp(last_slot, timestamp)?;
885 }
886 self.root_slot = new_root;
887 self.votes = new_state;
888 Ok(())
889 }
890
891 pub fn process_vote(
892 &mut self,
893 vote: &Vote,
894 slot_hashes: &[SlotHash],
895 epoch: Epoch,
896 feature_set: Option<&FeatureSet>,
897 ) -> Result<(), VoteError> {
898 if vote.slots.is_empty() {
899 return Err(VoteError::EmptySlots);
900 }
901 let filtered_vote_slots = feature_set.and_then(|feature_set| {
902 if feature_set.is_active(&filter_votes_outside_slot_hashes::id()) {
903 let earliest_slot_in_history =
904 slot_hashes.last().map(|(slot, _hash)| *slot).unwrap_or(0);
905 Some(
906 vote.slots
907 .iter()
908 .filter(|slot| **slot >= earliest_slot_in_history)
909 .cloned()
910 .collect::<Vec<Slot>>(),
911 )
912 } else {
913 None
914 }
915 });
916
917 let vote_slots = filtered_vote_slots.as_ref().unwrap_or(&vote.slots);
918 if vote_slots.is_empty() {
919 return Err(VoteError::VotesTooOldAllFiltered);
920 }
921
922 self.check_slots_are_valid(vote_slots, &vote.hash, slot_hashes)?;
923
924 vote_slots
925 .iter()
926 .for_each(|s| self.process_next_vote_slot(*s, epoch));
927 Ok(())
928 }
929
930 pub fn process_next_vote_slot(&mut self, next_vote_slot: Slot, epoch: Epoch) {
931 if self
933 .last_voted_slot()
934 .map_or(false, |last_voted_slot| next_vote_slot <= last_voted_slot)
935 {
936 return;
937 }
938
939 let vote = Lockout::new(next_vote_slot);
940
941 self.pop_expired_votes(next_vote_slot);
942
943 if self.votes.len() == MAX_LOCKOUT_HISTORY {
945 let vote = self.votes.pop_front().unwrap();
946 self.root_slot = Some(vote.slot);
947
948 self.increment_credits(epoch, 1);
949 }
950 self.votes.push_back(vote);
951 self.double_lockouts();
952 }
953
954 pub fn increment_credits(&mut self, epoch: Epoch, credits: u64) {
956 if self.epoch_credits.is_empty() {
960 self.epoch_credits.push((epoch, 0, 0));
961 } else if epoch != self.epoch_credits.last().unwrap().0 {
962 let (_, credits, prev_credits) = *self.epoch_credits.last().unwrap();
963
964 if credits != prev_credits {
965 self.epoch_credits.push((epoch, credits, credits));
968 } else {
969 self.epoch_credits.last_mut().unwrap().0 = epoch;
971 }
972
973 if self.epoch_credits.len() > MAX_EPOCH_CREDITS_HISTORY {
975 self.epoch_credits.remove(0);
976 }
977 }
978
979 self.epoch_credits.last_mut().unwrap().1 += credits;
980 }
981
982 pub fn process_vote_unchecked(&mut self, vote: Vote) {
984 let slot_hashes: Vec<_> = vote.slots.iter().rev().map(|x| (*x, vote.hash)).collect();
985 let _ignored = self.process_vote(&vote, &slot_hashes, self.current_epoch(), None);
986 }
987
988 #[cfg(test)]
989 pub fn process_slot_votes_unchecked(&mut self, slots: &[Slot]) {
990 for slot in slots {
991 self.process_slot_vote_unchecked(*slot);
992 }
993 }
994
995 pub fn process_slot_vote_unchecked(&mut self, slot: Slot) {
996 self.process_vote_unchecked(Vote::new(vec![slot], Hash::default()));
997 }
998
999 pub fn nth_recent_vote(&self, position: usize) -> Option<&Lockout> {
1000 if position < self.votes.len() {
1001 let pos = self.votes.len() - 1 - position;
1002 self.votes.get(pos)
1003 } else {
1004 None
1005 }
1006 }
1007
1008 pub fn last_lockout(&self) -> Option<&Lockout> {
1009 self.votes.back()
1010 }
1011
1012 pub fn last_voted_slot(&self) -> Option<Slot> {
1013 self.last_lockout().map(|v| v.slot)
1014 }
1015
1016 pub fn tower(&self) -> Vec<Slot> {
1019 self.votes.iter().map(|v| v.slot).collect()
1020 }
1021
1022 pub fn current_epoch(&self) -> Epoch {
1023 if self.epoch_credits.is_empty() {
1024 0
1025 } else {
1026 self.epoch_credits.last().unwrap().0
1027 }
1028 }
1029
1030 pub fn credits(&self) -> u64 {
1033 if self.epoch_credits.is_empty() {
1034 0
1035 } else {
1036 self.epoch_credits.last().unwrap().1
1037 }
1038 }
1039
1040 pub fn epoch_credits(&self) -> &Vec<(Epoch, u64, u64)> {
1046 &self.epoch_credits
1047 }
1048
1049 fn set_new_authorized_voter<F>(
1050 &mut self,
1051 authorized_pubkey: &Pubkey,
1052 current_epoch: Epoch,
1053 target_epoch: Epoch,
1054 verify: F,
1055 ) -> Result<(), InstructionError>
1056 where
1057 F: Fn(Pubkey) -> Result<(), InstructionError>,
1058 {
1059 let epoch_authorized_voter = self.get_and_update_authorized_voter(current_epoch)?;
1060 verify(epoch_authorized_voter)?;
1061
1062 if self.authorized_voters.contains(target_epoch) {
1068 return Err(VoteError::TooSoonToReauthorize.into());
1069 }
1070
1071 let (latest_epoch, latest_authorized_pubkey) = self
1073 .authorized_voters
1074 .last()
1075 .ok_or(InstructionError::InvalidAccountData)?;
1076
1077 if latest_authorized_pubkey != authorized_pubkey {
1081 let epoch_of_last_authorized_switch =
1083 self.prior_voters.last().map(|range| range.2).unwrap_or(0);
1084
1085 assert!(target_epoch > *latest_epoch);
1092
1093 self.prior_voters.append((
1095 *latest_authorized_pubkey,
1096 epoch_of_last_authorized_switch,
1097 target_epoch,
1098 ));
1099 }
1100
1101 self.authorized_voters
1102 .insert(target_epoch, *authorized_pubkey);
1103
1104 Ok(())
1105 }
1106
1107 fn get_and_update_authorized_voter(
1108 &mut self,
1109 current_epoch: Epoch,
1110 ) -> Result<Pubkey, InstructionError> {
1111 let pubkey = self
1112 .authorized_voters
1113 .get_and_cache_authorized_voter_for_epoch(current_epoch)
1114 .ok_or(InstructionError::InvalidAccountData)?;
1115 self.authorized_voters
1116 .purge_authorized_voters(current_epoch);
1117 Ok(pubkey)
1118 }
1119
1120 fn pop_expired_votes(&mut self, next_vote_slot: Slot) {
1125 while let Some(vote) = self.last_lockout() {
1126 if !vote.is_locked_out_at_slot(next_vote_slot) {
1127 self.votes.pop_back();
1128 } else {
1129 break;
1130 }
1131 }
1132 }
1133
1134 fn double_lockouts(&mut self) {
1135 let stack_depth = self.votes.len();
1136 for (i, v) in self.votes.iter_mut().enumerate() {
1137 if stack_depth > i + v.confirmation_count as usize {
1140 v.confirmation_count += 1;
1141 }
1142 }
1143 }
1144
1145 pub fn process_timestamp(
1146 &mut self,
1147 slot: Slot,
1148 timestamp: UnixTimestamp,
1149 ) -> Result<(), VoteError> {
1150 if (slot < self.last_timestamp.slot || timestamp < self.last_timestamp.timestamp)
1151 || (slot == self.last_timestamp.slot
1152 && BlockTimestamp { slot, timestamp } != self.last_timestamp
1153 && self.last_timestamp.slot != 0)
1154 {
1155 return Err(VoteError::TimestampTooOld);
1156 }
1157 self.last_timestamp = BlockTimestamp { slot, timestamp };
1158 Ok(())
1159 }
1160
1161 pub fn is_correct_size_and_initialized(data: &[u8]) -> bool {
1162 const VERSION_OFFSET: usize = 4;
1163 data.len() == VoteState::size_of()
1164 && data[VERSION_OFFSET..VERSION_OFFSET + DEFAULT_PRIOR_VOTERS_OFFSET]
1165 != [0; DEFAULT_PRIOR_VOTERS_OFFSET]
1166 }
1167}
1168
1169pub fn authorize<S: std::hash::BuildHasher>(
1173 vote_account: &mut BorrowedAccount,
1174 authorized: &Pubkey,
1175 vote_authorize: VoteAuthorize,
1176 signers: &HashSet<Pubkey, S>,
1177 clock: &Clock,
1178 feature_set: &FeatureSet,
1179) -> Result<(), InstructionError> {
1180 let mut vote_state: VoteState = vote_account
1181 .get_state::<VoteStateVersions>()?
1182 .convert_to_current();
1183
1184 match vote_authorize {
1185 VoteAuthorize::Voter => {
1186 let authorized_withdrawer_signer = if feature_set
1187 .is_active(&feature_set::vote_withdraw_authority_may_change_authorized_voter::id())
1188 {
1189 verify_authorized_signer(&vote_state.authorized_withdrawer, signers).is_ok()
1190 } else {
1191 false
1192 };
1193
1194 vote_state.set_new_authorized_voter(
1195 authorized,
1196 clock.epoch,
1197 clock.leader_schedule_epoch + 1,
1198 |epoch_authorized_voter| {
1199 if authorized_withdrawer_signer {
1201 Ok(())
1202 } else {
1203 verify_authorized_signer(&epoch_authorized_voter, signers)
1204 }
1205 },
1206 )?;
1207 }
1208 VoteAuthorize::Withdrawer => {
1209 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
1211 vote_state.authorized_withdrawer = *authorized;
1212 }
1213 }
1214
1215 vote_account.set_state(&VoteStateVersions::new_current(vote_state), feature_set)
1216}
1217
1218pub fn update_validator_identity<S: std::hash::BuildHasher>(
1220 vote_account: &mut BorrowedAccount,
1221 node_pubkey: &Pubkey,
1222 signers: &HashSet<Pubkey, S>,
1223 feature_set: &FeatureSet,
1224) -> Result<(), InstructionError> {
1225 let mut vote_state: VoteState = vote_account
1226 .get_state::<VoteStateVersions>()?
1227 .convert_to_current();
1228
1229 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
1231
1232 verify_authorized_signer(node_pubkey, signers)?;
1234
1235 vote_state.node_pubkey = *node_pubkey;
1236
1237 vote_account.set_state(&VoteStateVersions::new_current(vote_state), feature_set)
1238}
1239
1240pub fn update_commission<S: std::hash::BuildHasher>(
1242 vote_account: &mut BorrowedAccount,
1243 commission: u8,
1244 signers: &HashSet<Pubkey, S>,
1245 feature_set: &FeatureSet,
1246) -> Result<(), InstructionError> {
1247 let mut vote_state: VoteState = vote_account
1248 .get_state::<VoteStateVersions>()?
1249 .convert_to_current();
1250
1251 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
1253
1254 vote_state.commission = commission;
1255
1256 vote_account.set_state(&VoteStateVersions::new_current(vote_state), feature_set)
1257}
1258
1259fn verify_authorized_signer<S: std::hash::BuildHasher>(
1260 authorized: &Pubkey,
1261 signers: &HashSet<Pubkey, S>,
1262) -> Result<(), InstructionError> {
1263 if signers.contains(authorized) {
1264 Ok(())
1265 } else {
1266 Err(InstructionError::MissingRequiredSignature)
1267 }
1268}
1269
1270#[allow(clippy::too_many_arguments)]
1272pub fn withdraw<S: std::hash::BuildHasher>(
1273 transaction_context: &TransactionContext,
1274 instruction_context: &InstructionContext,
1275 vote_account_index: u16,
1276 lamports: u64,
1277 to_account_index: u16,
1278 signers: &HashSet<Pubkey, S>,
1279 rent_sysvar: Option<&Rent>,
1280 clock: Option<&Clock>,
1281 feature_set: &FeatureSet,
1282) -> Result<(), InstructionError> {
1283 let mut vote_account = instruction_context
1286 .try_borrow_instruction_account(transaction_context, vote_account_index)?;
1287 let vote_state: VoteState = vote_account
1288 .get_state::<VoteStateVersions>()?
1289 .convert_to_current();
1290
1291 verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
1292
1293 let remaining_balance = vote_account
1294 .get_lamports()
1295 .checked_sub(lamports)
1296 .ok_or(InstructionError::InsufficientFunds)?;
1297
1298 if remaining_balance == 0 {
1299 let reject_active_vote_account_close = clock
1300 .zip(vote_state.epoch_credits.last())
1301 .map(|(clock, (last_epoch_with_credits, _, _))| {
1302 let current_epoch = clock.epoch;
1303 current_epoch.saturating_sub(*last_epoch_with_credits) < 2
1307 })
1308 .unwrap_or(false);
1309
1310 if reject_active_vote_account_close {
1311 return Err(InstructionError::InvalidAccountData);
1314 } else {
1315 vote_account.set_state(
1319 &VoteStateVersions::new_current(VoteState::default()),
1320 feature_set,
1321 )?;
1322 }
1323 } else if let Some(rent_sysvar) = rent_sysvar {
1324 let min_rent_exempt_balance = rent_sysvar.minimum_balance(vote_account.get_data().len());
1325 if remaining_balance < min_rent_exempt_balance {
1326 return Err(InstructionError::InsufficientFunds);
1327 }
1328 }
1329
1330 vote_account.checked_sub_lamports(lamports, feature_set)?;
1331 drop(vote_account);
1332 let mut to_account = instruction_context
1335 .try_borrow_instruction_account(transaction_context, to_account_index)?;
1336 to_account.checked_add_lamports(lamports, feature_set)?;
1337 Ok(())
1338}
1339
1340pub fn initialize_account<S: std::hash::BuildHasher>(
1344 vote_account: &mut BorrowedAccount,
1345 vote_init: &VoteInit,
1346 signers: &HashSet<Pubkey, S>,
1347 clock: &Clock,
1348 feature_set: &FeatureSet,
1349) -> Result<(), InstructionError> {
1350 if vote_account.get_data().len() != VoteState::size_of() {
1351 return Err(InstructionError::InvalidAccountData);
1352 }
1353 let versioned = vote_account.get_state::<VoteStateVersions>()?;
1354
1355 if !versioned.is_uninitialized() {
1356 return Err(InstructionError::AccountAlreadyInitialized);
1357 }
1358
1359 verify_authorized_signer(&vote_init.node_pubkey, signers)?;
1361
1362 vote_account.set_state(
1363 &VoteStateVersions::new_current(VoteState::new(vote_init, clock)),
1364 feature_set,
1365 )
1366}
1367
1368fn verify_and_get_vote_state<S: std::hash::BuildHasher>(
1369 vote_account: &BorrowedAccount,
1370 clock: &Clock,
1371 signers: &HashSet<Pubkey, S>,
1372) -> Result<VoteState, InstructionError> {
1373 let versioned = vote_account.get_state::<VoteStateVersions>()?;
1374
1375 if versioned.is_uninitialized() {
1376 return Err(InstructionError::UninitializedAccount);
1377 }
1378
1379 let mut vote_state = versioned.convert_to_current();
1380 let authorized_voter = vote_state.get_and_update_authorized_voter(clock.epoch)?;
1381 verify_authorized_signer(&authorized_voter, signers)?;
1382
1383 Ok(vote_state)
1384}
1385
1386pub fn process_vote<S: std::hash::BuildHasher>(
1387 vote_account: &mut BorrowedAccount,
1388 slot_hashes: &[SlotHash],
1389 clock: &Clock,
1390 vote: &Vote,
1391 signers: &HashSet<Pubkey, S>,
1392 feature_set: &FeatureSet,
1393) -> Result<(), InstructionError> {
1394 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1395
1396 vote_state.process_vote(vote, slot_hashes, clock.epoch, Some(feature_set))?;
1397 if let Some(timestamp) = vote.timestamp {
1398 vote.slots
1399 .iter()
1400 .max()
1401 .ok_or(VoteError::EmptySlots)
1402 .and_then(|slot| vote_state.process_timestamp(*slot, timestamp))?;
1403 }
1404 vote_account.set_state(&VoteStateVersions::new_current(vote_state), feature_set)
1405}
1406
1407pub fn process_vote_state_update<S: std::hash::BuildHasher>(
1408 vote_account: &mut BorrowedAccount,
1409 slot_hashes: &[SlotHash],
1410 clock: &Clock,
1411 mut vote_state_update: VoteStateUpdate,
1412 signers: &HashSet<Pubkey, S>,
1413 feature_set: &FeatureSet,
1414) -> Result<(), InstructionError> {
1415 let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?;
1416 vote_state.check_update_vote_state_slots_are_valid(&mut vote_state_update, slot_hashes)?;
1417 vote_state.process_new_vote_state(
1418 vote_state_update.lockouts,
1419 vote_state_update.root,
1420 vote_state_update.timestamp,
1421 clock.epoch,
1422 Some(feature_set),
1423 )?;
1424 vote_account.set_state(&VoteStateVersions::new_current(vote_state), feature_set)
1425}
1426
1427pub fn create_account_with_authorized(
1428 node_pubkey: &Pubkey,
1429 authorized_voter: &Pubkey,
1430 authorized_withdrawer: &Pubkey,
1431 commission: u8,
1432 lamports: u64,
1433) -> AccountSharedData {
1434 let mut vote_account = AccountSharedData::new(lamports, VoteState::size_of(), &id());
1435
1436 let vote_state = VoteState::new(
1437 &VoteInit {
1438 node_pubkey: *node_pubkey,
1439 authorized_voter: *authorized_voter,
1440 authorized_withdrawer: *authorized_withdrawer,
1441 commission,
1442 },
1443 &Clock::default(),
1444 );
1445
1446 let versioned = VoteStateVersions::new_current(vote_state);
1447 VoteState::to(&versioned, &mut vote_account).unwrap();
1448
1449 vote_account
1450}
1451
1452pub fn create_account(
1454 vote_pubkey: &Pubkey,
1455 node_pubkey: &Pubkey,
1456 commission: u8,
1457 lamports: u64,
1458) -> AccountSharedData {
1459 create_account_with_authorized(node_pubkey, vote_pubkey, vote_pubkey, commission, lamports)
1460}