1#[cfg(feature = "dev-context-only-utils")]
4use arbitrary::Arbitrary;
5#[cfg(feature = "serde")]
6use serde_derive::{Deserialize, Serialize};
7#[cfg(feature = "frozen-abi")]
8use atlas_frozen_abi_macro::AbiExample;
9use {
10 crate::authorized_voters::AuthorizedVoters,
11 atlas_clock::{Epoch, Slot, UnixTimestamp},
12 atlas_pubkey::Pubkey,
13 atlas_rent::Rent,
14 std::{collections::VecDeque, fmt::Debug},
15};
16#[cfg(test)]
17use {arbitrary::Unstructured, atlas_epoch_schedule::MAX_LEADER_SCHEDULE_EPOCH_OFFSET};
18
19mod vote_state_0_23_5;
20pub mod vote_state_1_14_11;
21pub use vote_state_1_14_11::*;
22pub mod vote_state_versions;
23pub use vote_state_versions::*;
24pub mod vote_state_v3;
25pub use vote_state_v3::VoteStateV3;
26pub mod vote_state_v4;
27pub use vote_state_v4::VoteStateV4;
28mod vote_instruction_data;
29pub use vote_instruction_data::*;
30#[cfg(any(target_os = "atlas", feature = "bincode"))]
31pub(crate) mod vote_state_deserialize;
32
33pub const BLS_PUBLIC_KEY_COMPRESSED_SIZE: usize = 48;
35
36pub const MAX_LOCKOUT_HISTORY: usize = 31;
38pub const INITIAL_LOCKOUT: usize = 2;
39
40pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
42
43const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 114;
45
46pub const VOTE_CREDITS_GRACE_SLOTS: u8 = 2;
48
49pub const VOTE_CREDITS_MAXIMUM_PER_SLOT: u8 = 16;
51
52#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
53#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
54#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
55#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
56pub struct Lockout {
57 slot: Slot,
58 confirmation_count: u32,
59}
60
61impl Lockout {
62 pub fn new(slot: Slot) -> Self {
63 Self::new_with_confirmation_count(slot, 1)
64 }
65
66 pub fn new_with_confirmation_count(slot: Slot, confirmation_count: u32) -> Self {
67 Self {
68 slot,
69 confirmation_count,
70 }
71 }
72
73 pub fn lockout(&self) -> u64 {
75 (INITIAL_LOCKOUT as u64).wrapping_pow(std::cmp::min(
76 self.confirmation_count(),
77 MAX_LOCKOUT_HISTORY as u32,
78 ))
79 }
80
81 pub fn last_locked_out_slot(&self) -> Slot {
85 self.slot.saturating_add(self.lockout())
86 }
87
88 pub fn is_locked_out_at_slot(&self, slot: Slot) -> bool {
89 self.last_locked_out_slot() >= slot
90 }
91
92 pub fn slot(&self) -> Slot {
93 self.slot
94 }
95
96 pub fn confirmation_count(&self) -> u32 {
97 self.confirmation_count
98 }
99
100 pub fn increase_confirmation_count(&mut self, by: u32) {
101 self.confirmation_count = self.confirmation_count.saturating_add(by)
102 }
103}
104
105#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
106#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
107#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
108#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
109pub struct LandedVote {
110 pub latency: u8,
114 pub lockout: Lockout,
115}
116
117impl LandedVote {
118 pub fn slot(&self) -> Slot {
119 self.lockout.slot
120 }
121
122 pub fn confirmation_count(&self) -> u32 {
123 self.lockout.confirmation_count
124 }
125}
126
127impl From<LandedVote> for Lockout {
128 fn from(landed_vote: LandedVote) -> Self {
129 landed_vote.lockout
130 }
131}
132
133impl From<Lockout> for LandedVote {
134 fn from(lockout: Lockout) -> Self {
135 Self {
136 latency: 0,
137 lockout,
138 }
139 }
140}
141
142#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
143#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
144#[derive(Debug, Default, PartialEq, Eq, Clone)]
145#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
146pub struct BlockTimestamp {
147 pub slot: Slot,
148 pub timestamp: UnixTimestamp,
149}
150
151const MAX_ITEMS: usize = 32;
153
154#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
155#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
156#[derive(Debug, PartialEq, Eq, Clone)]
157#[cfg_attr(feature = "dev-context-only-utils", derive(Arbitrary))]
158pub struct CircBuf<I> {
159 buf: [I; MAX_ITEMS],
160 idx: usize,
162 is_empty: bool,
163}
164
165impl<I: Default + Copy> Default for CircBuf<I> {
166 fn default() -> Self {
167 Self {
168 buf: [I::default(); MAX_ITEMS],
169 idx: MAX_ITEMS
170 .checked_sub(1)
171 .expect("`MAX_ITEMS` should be positive"),
172 is_empty: true,
173 }
174 }
175}
176
177impl<I> CircBuf<I> {
178 pub fn append(&mut self, item: I) {
179 self.idx = self
181 .idx
182 .checked_add(1)
183 .and_then(|idx| idx.checked_rem(MAX_ITEMS))
184 .expect("`self.idx` should be < `MAX_ITEMS` which should be non-zero");
185
186 self.buf[self.idx] = item;
187 self.is_empty = false;
188 }
189
190 pub fn buf(&self) -> &[I; MAX_ITEMS] {
191 &self.buf
192 }
193
194 pub fn last(&self) -> Option<&I> {
195 if !self.is_empty {
196 self.buf.get(self.idx)
197 } else {
198 None
199 }
200 }
201}
202
203#[cfg(feature = "serde")]
204pub mod serde_compact_vote_state_update {
205 use {
206 super::*,
207 crate::state::Lockout,
208 serde::{Deserialize, Deserializer, Serialize, Serializer},
209 atlas_hash::Hash,
210 atlas_serde_varint as serde_varint, atlas_short_vec as short_vec,
211 };
212
213 #[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
214 #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
215 struct LockoutOffset {
216 #[serde(with = "serde_varint")]
217 offset: Slot,
218 confirmation_count: u8,
219 }
220
221 #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
222 struct CompactVoteStateUpdate {
223 root: Slot,
224 #[serde(with = "short_vec")]
225 lockout_offsets: Vec<LockoutOffset>,
226 hash: Hash,
227 timestamp: Option<UnixTimestamp>,
228 }
229
230 pub fn serialize<S>(
231 vote_state_update: &VoteStateUpdate,
232 serializer: S,
233 ) -> Result<S::Ok, S::Error>
234 where
235 S: Serializer,
236 {
237 let lockout_offsets = vote_state_update.lockouts.iter().scan(
238 vote_state_update.root.unwrap_or_default(),
239 |slot, lockout| {
240 let Some(offset) = lockout.slot().checked_sub(*slot) else {
241 return Some(Err(serde::ser::Error::custom("Invalid vote lockout")));
242 };
243 let Ok(confirmation_count) = u8::try_from(lockout.confirmation_count()) else {
244 return Some(Err(serde::ser::Error::custom("Invalid confirmation count")));
245 };
246 let lockout_offset = LockoutOffset {
247 offset,
248 confirmation_count,
249 };
250 *slot = lockout.slot();
251 Some(Ok(lockout_offset))
252 },
253 );
254 let compact_vote_state_update = CompactVoteStateUpdate {
255 root: vote_state_update.root.unwrap_or(Slot::MAX),
256 lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
257 hash: vote_state_update.hash,
258 timestamp: vote_state_update.timestamp,
259 };
260 compact_vote_state_update.serialize(serializer)
261 }
262
263 pub fn deserialize<'de, D>(deserializer: D) -> Result<VoteStateUpdate, D::Error>
264 where
265 D: Deserializer<'de>,
266 {
267 let CompactVoteStateUpdate {
268 root,
269 lockout_offsets,
270 hash,
271 timestamp,
272 } = CompactVoteStateUpdate::deserialize(deserializer)?;
273 let root = (root != Slot::MAX).then_some(root);
274 let lockouts =
275 lockout_offsets
276 .iter()
277 .scan(root.unwrap_or_default(), |slot, lockout_offset| {
278 *slot = match slot.checked_add(lockout_offset.offset) {
279 None => {
280 return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
281 }
282 Some(slot) => slot,
283 };
284 let lockout = Lockout::new_with_confirmation_count(
285 *slot,
286 u32::from(lockout_offset.confirmation_count),
287 );
288 Some(Ok(lockout))
289 });
290 Ok(VoteStateUpdate {
291 root,
292 lockouts: lockouts.collect::<Result<_, _>>()?,
293 hash,
294 timestamp,
295 })
296 }
297}
298
299#[cfg(feature = "serde")]
300pub mod serde_tower_sync {
301 use {
302 super::*,
303 crate::state::Lockout,
304 serde::{Deserialize, Deserializer, Serialize, Serializer},
305 atlas_hash::Hash,
306 atlas_serde_varint as serde_varint, atlas_short_vec as short_vec,
307 };
308
309 #[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
310 #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
311 struct LockoutOffset {
312 #[serde(with = "serde_varint")]
313 offset: Slot,
314 confirmation_count: u8,
315 }
316
317 #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
318 struct CompactTowerSync {
319 root: Slot,
320 #[serde(with = "short_vec")]
321 lockout_offsets: Vec<LockoutOffset>,
322 hash: Hash,
323 timestamp: Option<UnixTimestamp>,
324 block_id: Hash,
325 }
326
327 pub fn serialize<S>(tower_sync: &TowerSync, serializer: S) -> Result<S::Ok, S::Error>
328 where
329 S: Serializer,
330 {
331 let lockout_offsets = tower_sync.lockouts.iter().scan(
332 tower_sync.root.unwrap_or_default(),
333 |slot, lockout| {
334 let Some(offset) = lockout.slot().checked_sub(*slot) else {
335 return Some(Err(serde::ser::Error::custom("Invalid vote lockout")));
336 };
337 let Ok(confirmation_count) = u8::try_from(lockout.confirmation_count()) else {
338 return Some(Err(serde::ser::Error::custom("Invalid confirmation count")));
339 };
340 let lockout_offset = LockoutOffset {
341 offset,
342 confirmation_count,
343 };
344 *slot = lockout.slot();
345 Some(Ok(lockout_offset))
346 },
347 );
348 let compact_tower_sync = CompactTowerSync {
349 root: tower_sync.root.unwrap_or(Slot::MAX),
350 lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
351 hash: tower_sync.hash,
352 timestamp: tower_sync.timestamp,
353 block_id: tower_sync.block_id,
354 };
355 compact_tower_sync.serialize(serializer)
356 }
357
358 pub fn deserialize<'de, D>(deserializer: D) -> Result<TowerSync, D::Error>
359 where
360 D: Deserializer<'de>,
361 {
362 let CompactTowerSync {
363 root,
364 lockout_offsets,
365 hash,
366 timestamp,
367 block_id,
368 } = CompactTowerSync::deserialize(deserializer)?;
369 let root = (root != Slot::MAX).then_some(root);
370 let lockouts =
371 lockout_offsets
372 .iter()
373 .scan(root.unwrap_or_default(), |slot, lockout_offset| {
374 *slot = match slot.checked_add(lockout_offset.offset) {
375 None => {
376 return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
377 }
378 Some(slot) => slot,
379 };
380 let lockout = Lockout::new_with_confirmation_count(
381 *slot,
382 u32::from(lockout_offset.confirmation_count),
383 );
384 Some(Ok(lockout))
385 });
386 Ok(TowerSync {
387 root,
388 lockouts: lockouts.collect::<Result<_, _>>()?,
389 hash,
390 timestamp,
391 block_id,
392 })
393 }
394}
395
396#[cfg(test)]
397mod tests {
398 use {
399 super::*,
400 crate::{error::VoteError, state::vote_state_0_23_5::VoteState0_23_5},
401 bincode::serialized_size,
402 core::mem::MaybeUninit,
403 itertools::Itertools,
404 rand::Rng,
405 atlas_clock::Clock,
406 atlas_hash::Hash,
407 atlas_instruction::error::InstructionError,
408 };
409
410 fn create_test_vote_state_v4(node_pubkey: Pubkey, root_slot: Slot) -> VoteStateV4 {
412 let votes = (1..32)
413 .map(|x| LandedVote {
414 latency: 0,
415 lockout: Lockout::new_with_confirmation_count(
416 u64::from(x).saturating_add(root_slot),
417 32_u32.saturating_sub(x),
418 ),
419 })
420 .collect();
421 VoteStateV4 {
422 node_pubkey,
423 root_slot: Some(root_slot),
424 votes,
425 ..VoteStateV4::default()
426 }
427 }
428
429 #[test]
430 fn test_vote_serialize_v3() {
431 let mut buffer: Vec<u8> = vec![0; VoteStateV3::size_of()];
432 let mut vote_state = VoteStateV3::default();
433 vote_state
434 .votes
435 .resize(MAX_LOCKOUT_HISTORY, LandedVote::default());
436 vote_state.root_slot = Some(1);
437 let versioned = VoteStateVersions::new_v3(vote_state);
438 assert!(VoteStateV3::serialize(&versioned, &mut buffer[0..4]).is_err());
439 VoteStateV3::serialize(&versioned, &mut buffer).unwrap();
440 assert_eq!(
441 VoteStateV3::deserialize(&buffer).unwrap(),
442 versioned.try_convert_to_v3().unwrap()
443 );
444 }
445
446 #[test]
447 fn test_vote_serialize_v4() {
448 let vote_pubkey_for_deserialize = Pubkey::new_unique();
451 let vote_pubkey_for_convert = Pubkey::new_unique();
452
453 let mut buffer: Vec<u8> = vec![0; VoteStateV4::size_of()];
454 let mut vote_state = VoteStateV4::default();
455 vote_state
456 .votes
457 .resize(MAX_LOCKOUT_HISTORY, LandedVote::default());
458 vote_state.root_slot = Some(1);
459 let versioned = VoteStateVersions::new_v4(vote_state);
460 assert!(VoteStateV4::serialize(&versioned, &mut buffer[0..4]).is_err());
461 VoteStateV4::serialize(&versioned, &mut buffer).unwrap();
462 assert_eq!(
463 VoteStateV4::deserialize(&buffer, &vote_pubkey_for_deserialize).unwrap(),
464 versioned
465 .try_convert_to_v4(&vote_pubkey_for_convert)
466 .unwrap()
467 );
468 }
469
470 #[test]
471 fn test_vote_deserialize_into_v3() {
472 let target_vote_state = VoteStateV3::default();
474 let vote_state_buf =
475 bincode::serialize(&VoteStateVersions::new_v3(target_vote_state.clone())).unwrap();
476
477 let mut test_vote_state = VoteStateV3::default();
478 VoteStateV3::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
479
480 assert_eq!(target_vote_state, test_vote_state);
481
482 let struct_bytes_x4 = std::mem::size_of::<VoteStateV3>() * 4;
485 for _ in 0..1000 {
486 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
487 let mut unstructured = Unstructured::new(&raw_data);
488
489 let target_vote_state_versions =
490 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
491 let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
492
493 if let Ok(target_vote_state) = target_vote_state_versions.try_convert_to_v3() {
495 let mut test_vote_state = VoteStateV3::default();
496 VoteStateV3::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap();
497
498 assert_eq!(target_vote_state, test_vote_state);
499 }
500 }
501 }
502
503 #[test]
504 fn test_vote_deserialize_into_v4() {
505 let vote_pubkey = Pubkey::new_unique();
506
507 let target_vote_state = VoteStateV4::default();
509 let vote_state_buf =
510 bincode::serialize(&VoteStateVersions::new_v4(target_vote_state.clone())).unwrap();
511
512 let mut test_vote_state = VoteStateV4::default();
513 VoteStateV4::deserialize_into(&vote_state_buf, &mut test_vote_state, &vote_pubkey).unwrap();
514
515 assert_eq!(target_vote_state, test_vote_state);
516
517 let struct_bytes_x4 = std::mem::size_of::<VoteStateV4>() * 4;
520 for _ in 0..1000 {
521 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
522 let mut unstructured = Unstructured::new(&raw_data);
523
524 let target_vote_state_versions =
525 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
526 let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
527 let target_vote_state = target_vote_state_versions
528 .try_convert_to_v4(&vote_pubkey)
529 .unwrap();
530
531 let mut test_vote_state = VoteStateV4::default();
532 VoteStateV4::deserialize_into(&vote_state_buf, &mut test_vote_state, &vote_pubkey)
533 .unwrap();
534
535 assert_eq!(target_vote_state, test_vote_state);
536 }
537 }
538
539 #[test]
540 fn test_vote_deserialize_into_error_v3() {
541 let target_vote_state = VoteStateV3::new_rand_for_tests(Pubkey::new_unique(), 42);
542 let mut vote_state_buf =
543 bincode::serialize(&VoteStateVersions::new_v3(target_vote_state.clone())).unwrap();
544 let len = vote_state_buf.len();
545 vote_state_buf.truncate(len - 1);
546
547 let mut test_vote_state = VoteStateV3::default();
548 VoteStateV3::deserialize_into(&vote_state_buf, &mut test_vote_state).unwrap_err();
549 assert_eq!(test_vote_state, VoteStateV3::default());
550 }
551
552 #[test]
553 fn test_vote_deserialize_into_error_v4() {
554 let vote_pubkey = Pubkey::new_unique();
555
556 let target_vote_state = create_test_vote_state_v4(Pubkey::new_unique(), 42);
557 let mut vote_state_buf =
558 bincode::serialize(&VoteStateVersions::new_v4(target_vote_state.clone())).unwrap();
559 let len = vote_state_buf.len();
560 vote_state_buf.truncate(len - 1);
561
562 let mut test_vote_state = VoteStateV4::default();
563 VoteStateV4::deserialize_into(&vote_state_buf, &mut test_vote_state, &vote_pubkey)
564 .unwrap_err();
565 assert_eq!(test_vote_state, VoteStateV4::default());
566 }
567
568 #[test]
569 fn test_vote_deserialize_into_uninit_v3() {
570 let target_vote_state = VoteStateV3::default();
572 let vote_state_buf =
573 bincode::serialize(&VoteStateVersions::new_v3(target_vote_state.clone())).unwrap();
574
575 let mut test_vote_state = MaybeUninit::uninit();
576 VoteStateV3::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state).unwrap();
577 let test_vote_state = unsafe { test_vote_state.assume_init() };
578
579 assert_eq!(target_vote_state, test_vote_state);
580
581 let struct_bytes_x4 = std::mem::size_of::<VoteStateV3>() * 4;
584 for _ in 0..1000 {
585 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
586 let mut unstructured = Unstructured::new(&raw_data);
587
588 let target_vote_state_versions =
589 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
590 let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
591
592 if let Ok(target_vote_state) = target_vote_state_versions.try_convert_to_v3() {
594 let mut test_vote_state = MaybeUninit::uninit();
595 VoteStateV3::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state)
596 .unwrap();
597 let test_vote_state = unsafe { test_vote_state.assume_init() };
598
599 assert_eq!(target_vote_state, test_vote_state);
600 }
601 }
602 }
603
604 #[test]
605 fn test_vote_deserialize_into_uninit_v4() {
606 let vote_pubkey = Pubkey::new_unique();
607
608 let target_vote_state = VoteStateV4::default();
610 let vote_state_buf =
611 bincode::serialize(&VoteStateVersions::new_v4(target_vote_state.clone())).unwrap();
612
613 let mut test_vote_state = MaybeUninit::uninit();
614 VoteStateV4::deserialize_into_uninit(&vote_state_buf, &mut test_vote_state, &vote_pubkey)
615 .unwrap();
616 let test_vote_state = unsafe { test_vote_state.assume_init() };
617
618 assert_eq!(target_vote_state, test_vote_state);
619
620 let struct_bytes_x4 = std::mem::size_of::<VoteStateV4>() * 4;
623 for _ in 0..1000 {
624 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
625 let mut unstructured = Unstructured::new(&raw_data);
626
627 let target_vote_state_versions =
628 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
629 let vote_state_buf = bincode::serialize(&target_vote_state_versions).unwrap();
630 let target_vote_state = target_vote_state_versions
631 .try_convert_to_v4(&Pubkey::default())
632 .unwrap();
633
634 let mut test_vote_state = MaybeUninit::uninit();
635 VoteStateV4::deserialize_into_uninit(
636 &vote_state_buf,
637 &mut test_vote_state,
638 &Pubkey::default(),
639 )
640 .unwrap();
641 let test_vote_state = unsafe { test_vote_state.assume_init() };
642
643 assert_eq!(target_vote_state, test_vote_state);
644 }
645 }
646
647 #[test]
648 fn test_vote_deserialize_into_uninit_nopanic_v3() {
649 let mut test_vote_state = MaybeUninit::uninit();
651 let e = VoteStateV3::deserialize_into_uninit(&[], &mut test_vote_state).unwrap_err();
652 assert_eq!(e, InstructionError::InvalidAccountData);
653
654 let serialized_len_x4 = serialized_size(&VoteStateV3::default()).unwrap() * 4;
656 let mut rng = rand::thread_rng();
657 for _ in 0..1000 {
658 let raw_data_length = rng.gen_range(1..serialized_len_x4);
659 let mut raw_data: Vec<u8> = (0..raw_data_length).map(|_| rng.gen::<u8>()).collect();
660
661 if raw_data_length >= 4 && rng.gen::<bool>() {
663 let tag = rng.gen::<u8>() % 4;
664 raw_data[0] = tag;
665 raw_data[1] = 0;
666 raw_data[2] = 0;
667 raw_data[3] = 0;
668 }
669
670 let mut test_vote_state = MaybeUninit::uninit();
673 let test_res = VoteStateV3::deserialize_into_uninit(&raw_data, &mut test_vote_state);
674
675 let bincode_res = bincode::deserialize::<VoteStateVersions>(&raw_data)
677 .map_err(|_| InstructionError::InvalidAccountData)
678 .and_then(|versioned| versioned.try_convert_to_v3());
679
680 if test_res.is_err() {
681 assert!(bincode_res.is_err());
682 } else {
683 let test_vote_state = unsafe { test_vote_state.assume_init() };
684 assert_eq!(test_vote_state, bincode_res.unwrap());
685 }
686 }
687 }
688
689 #[test]
690 fn test_vote_deserialize_into_uninit_nopanic_v4() {
691 let vote_pubkey = Pubkey::new_unique();
692
693 let mut test_vote_state = MaybeUninit::uninit();
695 let e = VoteStateV4::deserialize_into_uninit(&[], &mut test_vote_state, &vote_pubkey)
696 .unwrap_err();
697 assert_eq!(e, InstructionError::InvalidAccountData);
698
699 let serialized_len_x4 = serialized_size(&VoteStateV4::default()).unwrap() * 4;
701 let mut rng = rand::thread_rng();
702 for _ in 0..1000 {
703 let raw_data_length = rng.gen_range(1..serialized_len_x4);
704 let mut raw_data: Vec<u8> = (0..raw_data_length).map(|_| rng.gen::<u8>()).collect();
705
706 if raw_data_length >= 4 && rng.gen::<bool>() {
708 let tag = rng.gen::<u8>() % 4;
709 raw_data[0] = tag;
710 raw_data[1] = 0;
711 raw_data[2] = 0;
712 raw_data[3] = 0;
713 }
714
715 let mut test_vote_state = MaybeUninit::uninit();
718 let test_res =
719 VoteStateV4::deserialize_into_uninit(&raw_data, &mut test_vote_state, &vote_pubkey);
720 let bincode_res = bincode::deserialize::<VoteStateVersions>(&raw_data)
721 .map(|versioned| versioned.try_convert_to_v4(&vote_pubkey).unwrap());
722
723 if test_res.is_err() {
724 assert!(bincode_res.is_err());
725 } else {
726 let test_vote_state = unsafe { test_vote_state.assume_init() };
727 assert_eq!(test_vote_state, bincode_res.unwrap());
728 }
729 }
730 }
731
732 #[test]
733 fn test_vote_deserialize_into_uninit_ill_sized_v3() {
734 let struct_bytes_x4 = std::mem::size_of::<VoteStateV3>() * 4;
736 for _ in 0..1000 {
737 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
738 let mut unstructured = Unstructured::new(&raw_data);
739
740 let original_vote_state_versions =
741 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
742 let original_buf = bincode::serialize(&original_vote_state_versions).unwrap();
743
744 if !matches!(original_vote_state_versions, VoteStateVersions::V4(_)) {
746 let mut truncated_buf = original_buf.clone();
747 let mut expanded_buf = original_buf.clone();
748
749 truncated_buf.resize(original_buf.len() - 8, 0);
750 expanded_buf.resize(original_buf.len() + 8, 0);
751
752 let mut test_vote_state = MaybeUninit::uninit();
754 let test_res =
755 VoteStateV3::deserialize_into_uninit(&truncated_buf, &mut test_vote_state);
756 let bincode_res = bincode::deserialize::<VoteStateVersions>(&truncated_buf)
759 .map_err(|_| InstructionError::InvalidAccountData)
760 .and_then(|versioned| versioned.try_convert_to_v3());
761
762 assert!(test_res.is_err());
763 assert!(bincode_res.is_err());
764
765 let mut test_vote_state = MaybeUninit::uninit();
767 VoteStateV3::deserialize_into_uninit(&expanded_buf, &mut test_vote_state).unwrap();
768 let bincode_res = bincode::deserialize::<VoteStateVersions>(&expanded_buf)
771 .map_err(|_| InstructionError::InvalidAccountData)
772 .and_then(|versioned| versioned.try_convert_to_v3());
773
774 let test_vote_state = unsafe { test_vote_state.assume_init() };
775 assert_eq!(test_vote_state, bincode_res.unwrap());
776 }
777 }
778 }
779
780 #[test]
781 fn test_vote_deserialize_into_uninit_ill_sized_v4() {
782 let vote_pubkey = Pubkey::new_unique();
783
784 let struct_bytes_x4 = std::mem::size_of::<VoteStateV4>() * 4;
786 for _ in 0..1000 {
787 let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
788 let mut unstructured = Unstructured::new(&raw_data);
789
790 let original_vote_state_versions =
791 VoteStateVersions::arbitrary(&mut unstructured).unwrap();
792 let original_buf = bincode::serialize(&original_vote_state_versions).unwrap();
793
794 let mut truncated_buf = original_buf.clone();
795 let mut expanded_buf = original_buf.clone();
796
797 truncated_buf.resize(original_buf.len() - 8, 0);
798 expanded_buf.resize(original_buf.len() + 8, 0);
799
800 let mut test_vote_state = MaybeUninit::uninit();
802 let test_res = VoteStateV4::deserialize_into_uninit(
803 &truncated_buf,
804 &mut test_vote_state,
805 &vote_pubkey,
806 );
807 let bincode_res = bincode::deserialize::<VoteStateVersions>(&truncated_buf)
808 .map(|versioned| versioned.try_convert_to_v4(&vote_pubkey).unwrap());
809
810 assert!(test_res.is_err());
811 assert!(bincode_res.is_err());
812
813 let mut test_vote_state = MaybeUninit::uninit();
815 VoteStateV4::deserialize_into_uninit(&expanded_buf, &mut test_vote_state, &vote_pubkey)
816 .unwrap();
817 let bincode_res = bincode::deserialize::<VoteStateVersions>(&expanded_buf)
818 .map(|versioned| versioned.try_convert_to_v4(&vote_pubkey).unwrap());
819
820 let test_vote_state = unsafe { test_vote_state.assume_init() };
821 assert_eq!(test_vote_state, bincode_res.unwrap());
822 }
823 }
824
825 #[test]
826 fn test_vote_state_epoch_credits() {
827 let mut vote_state = VoteStateV3::default();
828
829 assert_eq!(vote_state.credits(), 0);
830 assert_eq!(vote_state.epoch_credits().clone(), vec![]);
831
832 let mut expected = vec![];
833 let mut credits = 0;
834 let epochs = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
835 for epoch in 0..epochs {
836 for _j in 0..epoch {
837 vote_state.increment_credits(epoch, 1);
838 credits += 1;
839 }
840 expected.push((epoch, credits, credits - epoch));
841 }
842
843 while expected.len() > MAX_EPOCH_CREDITS_HISTORY {
844 expected.remove(0);
845 }
846
847 assert_eq!(vote_state.credits(), credits);
848 assert_eq!(vote_state.epoch_credits().clone(), expected);
849 }
850
851 #[test]
852 fn test_vote_state_epoch0_no_credits() {
853 let mut vote_state = VoteStateV3::default();
854
855 assert_eq!(vote_state.epoch_credits().len(), 0);
856 vote_state.increment_credits(1, 1);
857 assert_eq!(vote_state.epoch_credits().len(), 1);
858
859 vote_state.increment_credits(2, 1);
860 assert_eq!(vote_state.epoch_credits().len(), 2);
861 }
862
863 #[test]
864 fn test_vote_state_increment_credits() {
865 let mut vote_state = VoteStateV3::default();
866
867 let credits = (MAX_EPOCH_CREDITS_HISTORY + 2) as u64;
868 for i in 0..credits {
869 vote_state.increment_credits(i, 1);
870 }
871 assert_eq!(vote_state.credits(), credits);
872 assert!(vote_state.epoch_credits().len() <= MAX_EPOCH_CREDITS_HISTORY);
873 }
874
875 #[test]
876 fn test_vote_process_timestamp() {
877 let (slot, timestamp) = (15, 1_575_412_285);
878 let mut vote_state = VoteStateV3 {
879 last_timestamp: BlockTimestamp { slot, timestamp },
880 ..VoteStateV3::default()
881 };
882
883 assert_eq!(
884 vote_state.process_timestamp(slot - 1, timestamp + 1),
885 Err(VoteError::TimestampTooOld)
886 );
887 assert_eq!(
888 vote_state.last_timestamp,
889 BlockTimestamp { slot, timestamp }
890 );
891 assert_eq!(
892 vote_state.process_timestamp(slot + 1, timestamp - 1),
893 Err(VoteError::TimestampTooOld)
894 );
895 assert_eq!(
896 vote_state.process_timestamp(slot, timestamp + 1),
897 Err(VoteError::TimestampTooOld)
898 );
899 assert_eq!(vote_state.process_timestamp(slot, timestamp), Ok(()));
900 assert_eq!(
901 vote_state.last_timestamp,
902 BlockTimestamp { slot, timestamp }
903 );
904 assert_eq!(vote_state.process_timestamp(slot + 1, timestamp), Ok(()));
905 assert_eq!(
906 vote_state.last_timestamp,
907 BlockTimestamp {
908 slot: slot + 1,
909 timestamp
910 }
911 );
912 assert_eq!(
913 vote_state.process_timestamp(slot + 2, timestamp + 1),
914 Ok(())
915 );
916 assert_eq!(
917 vote_state.last_timestamp,
918 BlockTimestamp {
919 slot: slot + 2,
920 timestamp: timestamp + 1
921 }
922 );
923
924 vote_state.last_timestamp = BlockTimestamp::default();
926 assert_eq!(vote_state.process_timestamp(0, timestamp), Ok(()));
927 }
928
929 #[test]
930 fn test_get_and_update_authorized_voter() {
931 let original_voter = Pubkey::new_unique();
932 let mut vote_state = VoteStateV3::new(
933 &VoteInit {
934 node_pubkey: original_voter,
935 authorized_voter: original_voter,
936 authorized_withdrawer: original_voter,
937 commission: 0,
938 },
939 &Clock::default(),
940 );
941
942 assert_eq!(vote_state.authorized_voters.len(), 1);
943 assert_eq!(
944 *vote_state.authorized_voters.first().unwrap().1,
945 original_voter
946 );
947
948 assert_eq!(
951 vote_state.get_and_update_authorized_voter(1).unwrap(),
952 original_voter
953 );
954
955 assert_eq!(
958 vote_state.get_and_update_authorized_voter(5).unwrap(),
959 original_voter
960 );
961
962 assert_eq!(vote_state.authorized_voters.len(), 1);
965 for i in 0..5 {
966 assert!(vote_state
967 .authorized_voters
968 .get_authorized_voter(i)
969 .is_none());
970 }
971
972 let new_authorized_voter = Pubkey::new_unique();
974 vote_state
975 .set_new_authorized_voter(&new_authorized_voter, 5, 7, |_| Ok(()))
976 .unwrap();
977
978 assert_eq!(
980 vote_state.get_and_update_authorized_voter(6).unwrap(),
981 original_voter
982 );
983
984 for i in 7..10 {
987 assert_eq!(
988 vote_state.get_and_update_authorized_voter(i).unwrap(),
989 new_authorized_voter
990 );
991 }
992 assert_eq!(vote_state.authorized_voters.len(), 1);
993 }
994
995 #[test]
996 fn test_set_new_authorized_voter() {
997 let original_voter = Pubkey::new_unique();
998 let epoch_offset = 15;
999 let mut vote_state = VoteStateV3::new(
1000 &VoteInit {
1001 node_pubkey: original_voter,
1002 authorized_voter: original_voter,
1003 authorized_withdrawer: original_voter,
1004 commission: 0,
1005 },
1006 &Clock::default(),
1007 );
1008
1009 assert!(vote_state.prior_voters.last().is_none());
1010
1011 let new_voter = Pubkey::new_unique();
1012 vote_state
1014 .set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(()))
1015 .unwrap();
1016
1017 assert_eq!(vote_state.prior_voters.idx, 0);
1018 assert_eq!(
1019 vote_state.prior_voters.last(),
1020 Some(&(original_voter, 0, epoch_offset))
1021 );
1022
1023 assert_eq!(
1025 vote_state.set_new_authorized_voter(&new_voter, 0, epoch_offset, |_| Ok(())),
1026 Err(VoteError::TooSoonToReauthorize.into())
1027 );
1028
1029 vote_state
1031 .set_new_authorized_voter(&new_voter, 2, 2 + epoch_offset, |_| Ok(()))
1032 .unwrap();
1033
1034 let new_voter2 = Pubkey::new_unique();
1036 vote_state
1037 .set_new_authorized_voter(&new_voter2, 3, 3 + epoch_offset, |_| Ok(()))
1038 .unwrap();
1039 assert_eq!(vote_state.prior_voters.idx, 1);
1040 assert_eq!(
1041 vote_state.prior_voters.last(),
1042 Some(&(new_voter, epoch_offset, 3 + epoch_offset))
1043 );
1044
1045 let new_voter3 = Pubkey::new_unique();
1046 vote_state
1047 .set_new_authorized_voter(&new_voter3, 6, 6 + epoch_offset, |_| Ok(()))
1048 .unwrap();
1049 assert_eq!(vote_state.prior_voters.idx, 2);
1050 assert_eq!(
1051 vote_state.prior_voters.last(),
1052 Some(&(new_voter2, 3 + epoch_offset, 6 + epoch_offset))
1053 );
1054
1055 vote_state
1057 .set_new_authorized_voter(&original_voter, 9, 9 + epoch_offset, |_| Ok(()))
1058 .unwrap();
1059
1060 for i in 9..epoch_offset {
1063 assert_eq!(
1064 vote_state.get_and_update_authorized_voter(i).unwrap(),
1065 original_voter
1066 );
1067 }
1068 for i in epoch_offset..3 + epoch_offset {
1069 assert_eq!(
1070 vote_state.get_and_update_authorized_voter(i).unwrap(),
1071 new_voter
1072 );
1073 }
1074 for i in 3 + epoch_offset..6 + epoch_offset {
1075 assert_eq!(
1076 vote_state.get_and_update_authorized_voter(i).unwrap(),
1077 new_voter2
1078 );
1079 }
1080 for i in 6 + epoch_offset..9 + epoch_offset {
1081 assert_eq!(
1082 vote_state.get_and_update_authorized_voter(i).unwrap(),
1083 new_voter3
1084 );
1085 }
1086 for i in 9 + epoch_offset..=10 + epoch_offset {
1087 assert_eq!(
1088 vote_state.get_and_update_authorized_voter(i).unwrap(),
1089 original_voter
1090 );
1091 }
1092 }
1093
1094 #[test]
1095 fn test_authorized_voter_is_locked_within_epoch() {
1096 let original_voter = Pubkey::new_unique();
1097 let mut vote_state = VoteStateV3::new(
1098 &VoteInit {
1099 node_pubkey: original_voter,
1100 authorized_voter: original_voter,
1101 authorized_withdrawer: original_voter,
1102 commission: 0,
1103 },
1104 &Clock::default(),
1105 );
1106
1107 let new_voter = Pubkey::new_unique();
1111 assert_eq!(
1112 vote_state.set_new_authorized_voter(&new_voter, 1, 1, |_| Ok(())),
1113 Err(VoteError::TooSoonToReauthorize.into())
1114 );
1115
1116 assert_eq!(vote_state.get_authorized_voter(1), Some(original_voter));
1117
1118 assert_eq!(
1120 vote_state.set_new_authorized_voter(&new_voter, 1, 2, |_| Ok(())),
1121 Ok(())
1122 );
1123
1124 assert_eq!(
1128 vote_state.set_new_authorized_voter(&original_voter, 3, 3, |_| Ok(())),
1129 Err(VoteError::TooSoonToReauthorize.into())
1130 );
1131
1132 assert_eq!(vote_state.get_authorized_voter(3), Some(new_voter));
1133 }
1134
1135 #[test]
1136 fn test_vote_state_v3_size_of() {
1137 let vote_state = VoteStateV3::get_max_sized_vote_state();
1138 let vote_state = VoteStateVersions::new_v3(vote_state);
1139 let size = serialized_size(&vote_state).unwrap();
1140 assert_eq!(VoteStateV3::size_of() as u64, size);
1141 }
1142
1143 #[test]
1144 fn test_vote_state_v4_size_of() {
1145 let vote_state = VoteStateV4::get_max_sized_vote_state();
1146 let vote_state = VoteStateVersions::new_v4(vote_state);
1147 let size = serialized_size(&vote_state).unwrap();
1148 assert!(size < VoteStateV4::size_of() as u64); }
1150
1151 #[test]
1152 fn test_vote_state_max_size() {
1153 let mut max_sized_data = vec![0; VoteStateV3::size_of()];
1154 let vote_state = VoteStateV3::get_max_sized_vote_state();
1155 let (start_leader_schedule_epoch, _) = vote_state.authorized_voters.last().unwrap();
1156 let start_current_epoch =
1157 start_leader_schedule_epoch - MAX_LEADER_SCHEDULE_EPOCH_OFFSET + 1;
1158
1159 let mut vote_state = Some(vote_state);
1160 for i in start_current_epoch..start_current_epoch + 2 * MAX_LEADER_SCHEDULE_EPOCH_OFFSET {
1161 vote_state.as_mut().map(|vote_state| {
1162 vote_state.set_new_authorized_voter(
1163 &Pubkey::new_unique(),
1164 i,
1165 i + MAX_LEADER_SCHEDULE_EPOCH_OFFSET,
1166 |_| Ok(()),
1167 )
1168 });
1169
1170 let versioned = VoteStateVersions::new_v3(vote_state.take().unwrap());
1171 VoteStateV3::serialize(&versioned, &mut max_sized_data).unwrap();
1172 vote_state = Some(versioned.try_convert_to_v3().unwrap());
1173 }
1174 }
1175
1176 #[test]
1177 fn test_default_vote_state_is_uninitialized() {
1178 assert!(VoteStateVersions::new_v3(VoteStateV3::default()).is_uninitialized());
1182 }
1183
1184 #[test]
1185 fn test_is_correct_size_and_initialized() {
1186 let mut vote_account_data = vec![0; VoteStateV3::size_of()];
1188 assert!(!VoteStateVersions::is_correct_size_and_initialized(
1189 &vote_account_data
1190 ));
1191
1192 let default_account_state = VoteStateVersions::new_v3(VoteStateV3::default());
1194 VoteStateV3::serialize(&default_account_state, &mut vote_account_data).unwrap();
1195 assert!(!VoteStateVersions::is_correct_size_and_initialized(
1196 &vote_account_data
1197 ));
1198
1199 let short_data = vec![1; DEFAULT_PRIOR_VOTERS_OFFSET];
1201 assert!(!VoteStateVersions::is_correct_size_and_initialized(
1202 &short_data
1203 ));
1204
1205 let mut large_vote_data = vec![1; 2 * VoteStateV3::size_of()];
1207 let default_account_state = VoteStateVersions::new_v3(VoteStateV3::default());
1208 VoteStateV3::serialize(&default_account_state, &mut large_vote_data).unwrap();
1209 assert!(!VoteStateVersions::is_correct_size_and_initialized(
1210 &vote_account_data
1211 ));
1212
1213 let vote_state = VoteStateV3::new(
1215 &VoteInit {
1216 node_pubkey: Pubkey::new_unique(),
1217 authorized_voter: Pubkey::new_unique(),
1218 authorized_withdrawer: Pubkey::new_unique(),
1219 commission: 0,
1220 },
1221 &Clock::default(),
1222 );
1223 let account_state = VoteStateVersions::new_v3(vote_state.clone());
1224 VoteStateV3::serialize(&account_state, &mut vote_account_data).unwrap();
1225 assert!(VoteStateVersions::is_correct_size_and_initialized(
1226 &vote_account_data
1227 ));
1228
1229 let old_vote_state = VoteState1_14_11::from(vote_state);
1231 let account_state = VoteStateVersions::V1_14_11(Box::new(old_vote_state));
1232 let mut vote_account_data = vec![0; VoteState1_14_11::size_of()];
1233 VoteStateV3::serialize(&account_state, &mut vote_account_data).unwrap();
1234 assert!(VoteStateVersions::is_correct_size_and_initialized(
1235 &vote_account_data
1236 ));
1237 }
1238
1239 #[test]
1240 fn test_minimum_balance() {
1241 let rent = atlas_rent::Rent::default();
1242 let minimum_balance = rent.minimum_balance(VoteStateV3::size_of());
1243 assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)
1245 }
1246
1247 #[test]
1248 fn test_serde_compact_vote_state_update() {
1249 let mut rng = rand::thread_rng();
1250 for _ in 0..5000 {
1251 run_serde_compact_vote_state_update(&mut rng);
1252 }
1253 }
1254
1255 fn run_serde_compact_vote_state_update<R: Rng>(rng: &mut R) {
1256 let lockouts: VecDeque<_> = std::iter::repeat_with(|| {
1257 let slot = 149_303_885_u64.saturating_add(rng.gen_range(0..10_000));
1258 let confirmation_count = rng.gen_range(0..33);
1259 Lockout::new_with_confirmation_count(slot, confirmation_count)
1260 })
1261 .take(32)
1262 .sorted_by_key(|lockout| lockout.slot())
1263 .collect();
1264 let root = rng.gen_ratio(1, 2).then(|| {
1265 lockouts[0]
1266 .slot()
1267 .checked_sub(rng.gen_range(0..1_000))
1268 .expect("All slots should be greater than 1_000")
1269 });
1270 let timestamp = rng.gen_ratio(1, 2).then(|| rng.gen());
1271 let hash = Hash::from(rng.gen::<[u8; 32]>());
1272 let vote_state_update = VoteStateUpdate {
1273 lockouts,
1274 root,
1275 hash,
1276 timestamp,
1277 };
1278 #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
1279 enum VoteInstruction {
1280 #[serde(with = "serde_compact_vote_state_update")]
1281 UpdateVoteState(VoteStateUpdate),
1282 UpdateVoteStateSwitch(
1283 #[serde(with = "serde_compact_vote_state_update")] VoteStateUpdate,
1284 Hash,
1285 ),
1286 }
1287 let vote = VoteInstruction::UpdateVoteState(vote_state_update.clone());
1288 let bytes = bincode::serialize(&vote).unwrap();
1289 assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1290 let hash = Hash::from(rng.gen::<[u8; 32]>());
1291 let vote = VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash);
1292 let bytes = bincode::serialize(&vote).unwrap();
1293 assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
1294 }
1295
1296 #[test]
1297 fn test_circbuf_oob() {
1298 let data: &[u8] = &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00];
1300 let circ_buf: CircBuf<()> = bincode::deserialize(data).unwrap();
1301 assert_eq!(circ_buf.last(), None);
1302 }
1303
1304 #[test]
1305 fn test_vote_state_v4_bls_pubkey_compressed() {
1306 let vote_pubkey = Pubkey::new_unique();
1307
1308 let run_test = |start, expected| {
1309 let versioned = VoteStateVersions::new_v4(start);
1310 let serialized = bincode::serialize(&versioned).unwrap();
1311 let deserialized = VoteStateV4::deserialize(&serialized, &vote_pubkey).unwrap();
1312 assert_eq!(deserialized.bls_pubkey_compressed, expected);
1313 };
1314
1315 let vote_state_none = VoteStateV4::default();
1317 assert_eq!(vote_state_none.bls_pubkey_compressed, None);
1318 run_test(vote_state_none, None);
1319
1320 let test_bls_key = [42u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE];
1322 let vote_state_some = VoteStateV4 {
1323 bls_pubkey_compressed: Some(test_bls_key),
1324 ..VoteStateV4::default()
1325 };
1326 assert_eq!(vote_state_some.bls_pubkey_compressed, Some(test_bls_key));
1327 run_test(vote_state_some, Some(test_bls_key));
1328 }
1329
1330 #[test]
1331 fn test_vote_state_version_conversion_bls_pubkey() {
1332 let vote_pubkey = Pubkey::new_unique();
1333
1334 let v0_23_5_state = VoteState0_23_5::default();
1336 let v0_23_5_versioned = VoteStateVersions::V0_23_5(Box::new(v0_23_5_state));
1337
1338 let v1_14_11_state = VoteState1_14_11::default();
1339 let v1_14_11_versioned = VoteStateVersions::V1_14_11(Box::new(v1_14_11_state));
1340
1341 let v3_state = VoteStateV3::default();
1342 let v3_versioned = VoteStateVersions::V3(Box::new(v3_state));
1343
1344 for versioned in [v0_23_5_versioned, v1_14_11_versioned, v3_versioned] {
1345 let converted = versioned.try_convert_to_v4(&vote_pubkey).unwrap();
1346 assert_eq!(converted.bls_pubkey_compressed, None);
1347 }
1348
1349 let test_bls_key = [128u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE];
1351 let v4_state = VoteStateV4 {
1352 bls_pubkey_compressed: Some(test_bls_key),
1353 ..VoteStateV4::default()
1354 };
1355 let v4_versioned = VoteStateVersions::V4(Box::new(v4_state));
1356 let converted = v4_versioned.try_convert_to_v4(&vote_pubkey).unwrap();
1357 assert_eq!(converted.bls_pubkey_compressed, Some(test_bls_key));
1358 }
1359}