Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 78 additions & 6 deletions vote-interface/src/state/vote_state_deserialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,56 @@ pub(crate) fn deserialize_into<T: Default>(
res
}

pub(crate) fn deserialize_vote_state_into_v1_14_11(
cursor: &mut Cursor<&[u8]>,
vote_state: *mut crate::state::vote_state_1_14_11::VoteState1_14_11,
) -> Result<(), InstructionError> {
// General safety note: we must use addr_of_mut! to access the `vote_state` fields as the value
// is assumed to be _uninitialized_, so creating references to the state or any of its inner
// fields is UB.

read_pubkey_into(
cursor,
// Safety: if vote_state is non-null, node_pubkey is guaranteed to be valid too
unsafe { addr_of_mut!((*vote_state).node_pubkey) },
)?;
read_pubkey_into(
cursor,
// Safety: if vote_state is non-null, authorized_withdrawer is guaranteed to be valid too
unsafe { addr_of_mut!((*vote_state).authorized_withdrawer) },
)?;
let commission = read_u8(cursor)?;
let votes = read_votes_as_lockouts(cursor)?; // `Vec<Lockout>`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove this comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels incredibly pedantic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha yeah but it's also not the correct type so it stood out :P

let root_slot = read_option_u64(cursor)?;
let authorized_voters = read_authorized_voters(cursor)?;

read_prior_voters_into(
cursor,
// Safety: if vote_state is non-null, prior_voters is guaranteed to be valid too
unsafe { addr_of_mut!((*vote_state).prior_voters) },
)?;

let epoch_credits = read_epoch_credits(cursor)?;
let last_timestamp = read_last_timestamp(cursor)?;

// Safety: if vote_state is non-null, all the fields are guaranteed to be
// valid pointers.
//
// Heap allocated collections - votes, authorized_voters, prior_voters, and epoch_credits -
// are guaranteed not to leak after this point as the VoteState1_14_11 is fully
// initialized and will be regularly dropped.
unsafe {
addr_of_mut!((*vote_state).commission).write(commission);
addr_of_mut!((*vote_state).votes).write(votes);
addr_of_mut!((*vote_state).root_slot).write(root_slot);
addr_of_mut!((*vote_state).authorized_voters).write(authorized_voters);
addr_of_mut!((*vote_state).epoch_credits).write(epoch_credits);
addr_of_mut!((*vote_state).last_timestamp).write(last_timestamp);
}

Ok(())
}

pub(super) fn deserialize_vote_state_into_v3(
cursor: &mut Cursor<&[u8]>,
vote_state: *mut VoteStateV3,
Expand All @@ -95,7 +145,13 @@ pub(super) fn deserialize_vote_state_into_v3(
let votes = read_votes(cursor, has_latency)?;
let root_slot = read_option_u64(cursor)?;
let authorized_voters = read_authorized_voters(cursor)?;
read_prior_voters_into(cursor, vote_state)?;

read_prior_voters_into(
cursor,
// Safety: if vote_state is non-null, prior_voters is guaranteed to be valid too
unsafe { addr_of_mut!((*vote_state).prior_voters) },
)?;

let epoch_credits = read_epoch_credits(cursor)?;
let last_timestamp = read_last_timestamp(cursor)?;

Expand Down Expand Up @@ -252,6 +308,23 @@ fn read_votes<T: AsRef<[u8]>>(
Ok(votes)
}

fn read_votes_as_lockouts<T: AsRef<[u8]>>(
cursor: &mut Cursor<T>,
) -> Result<VecDeque<Lockout>, InstructionError> {
let vote_count = read_u64(cursor)? as usize;
let mut votes = VecDeque::with_capacity(vote_count.min(MAX_LOCKOUT_HISTORY));

for _ in 0..vote_count {
let slot = read_u64(cursor)?;
let confirmation_count = read_u32(cursor)?;
let lockout = Lockout::new_with_confirmation_count(slot, confirmation_count);

votes.push_back(lockout);
}

Ok(votes)
}

fn read_authorized_voters<T: AsRef<[u8]>>(
cursor: &mut Cursor<T>,
) -> Result<AuthorizedVoters, InstructionError> {
Expand All @@ -269,11 +342,10 @@ fn read_authorized_voters<T: AsRef<[u8]>>(

fn read_prior_voters_into<T: AsRef<[u8]>>(
cursor: &mut Cursor<T>,
vote_state: *mut VoteStateV3,
prior_voters: *mut crate::state::CircBuf<(Pubkey, Epoch, Epoch)>,
) -> Result<(), InstructionError> {
// Safety: if vote_state is non-null, prior_voters is guaranteed to be valid too
// Safety: if prior_voters is non-null, its buf field is guaranteed to be valid too
unsafe {
let prior_voters = addr_of_mut!((*vote_state).prior_voters);
let prior_voters_buf = addr_of_mut!((*prior_voters).buf) as *mut (Pubkey, Epoch, Epoch);

for i in 0..MAX_ITEMS {
Expand All @@ -286,8 +358,8 @@ fn read_prior_voters_into<T: AsRef<[u8]>>(
.write((prior_voter, from_epoch, until_epoch));
}

(*vote_state).prior_voters.idx = read_u64(cursor)? as usize;
(*vote_state).prior_voters.is_empty = read_bool(cursor)?;
(*prior_voters).idx = read_u64(cursor)? as usize;
(*prior_voters).is_empty = read_bool(cursor)?;
}
Ok(())
}
Expand Down
92 changes: 91 additions & 1 deletion vote-interface/src/state/vote_state_versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ use crate::state::{
};
#[cfg(test)]
use arbitrary::{Arbitrary, Unstructured};
#[cfg(any(target_os = "solana", feature = "bincode"))]
use solana_instruction_error::InstructionError;
#[cfg(any(test, all(not(target_os = "solana"), feature = "bincode")))]
use {
crate::{
authorized_voters::AuthorizedVoters,
state::{CircBuf, LandedVote, Lockout},
},
solana_instruction::error::InstructionError,
solana_pubkey::Pubkey,
std::collections::VecDeque,
};
Expand Down Expand Up @@ -190,6 +191,58 @@ impl VoteStateVersions {
|| VoteStateV3::is_correct_size_and_initialized(data)
|| VoteState1_14_11::is_correct_size_and_initialized(data)
}

/// Deserializes the input buffer directly into the appropriate `VoteStateVersions` variant.
///
/// V0_23_5 is not supported. All other versions (V1_14_11, V3, V4) are deserialized as-is
/// without any version coercion.
#[cfg(any(target_os = "solana", feature = "bincode"))]
pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
use {
crate::state::vote_state_deserialize::{
deserialize_vote_state_into_v1_14_11, deserialize_vote_state_into_v3,
deserialize_vote_state_into_v4, SourceVersion,
},
std::mem::MaybeUninit,
};

let mut cursor = std::io::Cursor::new(input);

let variant = solana_serialize_utils::cursor::read_u32(&mut cursor)?;
match variant {
// V0_23_5 not supported.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's return InstructionError::UninitializedAccount for this instead of InvalidAccountData

0 => Err(InstructionError::UninitializedAccount),
// V1_14_11
1 => {
let mut vote_state = Box::new(MaybeUninit::uninit());
deserialize_vote_state_into_v1_14_11(&mut cursor, vote_state.as_mut_ptr())?;
let vote_state =
unsafe { Box::from_raw(Box::into_raw(vote_state) as *mut VoteState1_14_11) };
Comment on lines +219 to +220
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like you can do this without the into/from

Suggested change
let vote_state =
unsafe { Box::from_raw(Box::into_raw(vote_state) as *mut VoteState1_14_11) };
let vote_state = unsafe { vote_state.assume_init() };

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can, but only for Rust 1.82 and above. I would have to reconfigure the MSRV checks on the workspace in order to allow vote-interface to move past everything else, or bump everything to 1.82.

Ok(VoteStateVersions::V1_14_11(vote_state))
}
// V3
2 => {
let mut vote_state = Box::new(MaybeUninit::uninit());
deserialize_vote_state_into_v3(&mut cursor, vote_state.as_mut_ptr(), true)?;
let vote_state =
unsafe { Box::from_raw(Box::into_raw(vote_state) as *mut VoteStateV3) };
Ok(VoteStateVersions::V3(vote_state))
}
// V4
3 => {
let mut vote_state = Box::new(MaybeUninit::uninit());
deserialize_vote_state_into_v4(
&mut cursor,
vote_state.as_mut_ptr(),
SourceVersion::V4,
)?;
let vote_state =
unsafe { Box::from_raw(Box::into_raw(vote_state) as *mut VoteStateV4) };
Ok(VoteStateVersions::V4(vote_state))
}
_ => Err(InstructionError::InvalidAccountData),
}
}
}

#[cfg(test)]
Expand All @@ -204,3 +257,40 @@ impl Arbitrary<'_> for VoteStateVersions {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_vote_state_versions_deserialize() {
let ser_deser = |original: VoteStateVersions| {
let serialized = bincode::serialize(&original).unwrap();
VoteStateVersions::deserialize(&serialized)
};

let v0_23_5 = VoteStateVersions::V0_23_5(Box::default());
assert_eq!(
ser_deser(v0_23_5),
Err(InstructionError::UninitializedAccount), // <-- v0_23_5 unsupported
);

let v1_14_11 = VoteStateVersions::V1_14_11(Box::default());
assert_eq!(
ser_deser(v1_14_11.clone()),
Ok(v1_14_11), // <-- Matches original
);

let v3 = VoteStateVersions::V3(Box::default());
assert_eq!(
ser_deser(v3.clone()),
Ok(v3), // <-- Matches original
);

let v4 = VoteStateVersions::V4(Box::default());
assert_eq!(
ser_deser(v4.clone()),
Ok(v4), // <-- Matches original
);
}
}