solana-program: only decode prior_voters if needed (#34972)
This commit is contained in:
parent
2455dc1a69
commit
c99427eb9e
|
@ -61,6 +61,15 @@ pub(crate) fn read_pubkey<T: AsRef<[u8]>>(
|
|||
Ok(Pubkey::from(buf))
|
||||
}
|
||||
|
||||
pub(crate) fn read_bool<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<bool, InstructionError> {
|
||||
let byte = read_u8(cursor)?;
|
||||
match byte {
|
||||
0 => Ok(false),
|
||||
1 => Ok(true),
|
||||
_ => Err(InstructionError::InvalidAccountData),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {super::*, rand::Rng, std::fmt::Debug};
|
||||
|
@ -115,6 +124,12 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_bool() {
|
||||
test_read(read_bool, false);
|
||||
test_read(read_bool, true);
|
||||
}
|
||||
|
||||
fn test_read<T: Debug + PartialEq + serde::Serialize + borsh0_10::BorshSerialize>(
|
||||
reader: fn(&mut Cursor<Vec<u8>>) -> Result<T, InstructionError>,
|
||||
test_value: T,
|
||||
|
|
|
@ -18,7 +18,7 @@ use {
|
|||
sysvar::clock::Clock,
|
||||
vote::{authorized_voters::AuthorizedVoters, error::VoteError},
|
||||
},
|
||||
bincode::{serialize_into, ErrorKind},
|
||||
bincode::{serialize_into, serialized_size, ErrorKind},
|
||||
serde_derive::{Deserialize, Serialize},
|
||||
std::{collections::VecDeque, fmt::Debug, io::Cursor},
|
||||
};
|
||||
|
@ -399,6 +399,12 @@ impl VoteState {
|
|||
input: &[u8],
|
||||
vote_state: &mut VoteState,
|
||||
) -> Result<(), InstructionError> {
|
||||
let minimum_size =
|
||||
serialized_size(vote_state).map_err(|_| InstructionError::InvalidAccountData)?;
|
||||
if (input.len() as u64) < minimum_size {
|
||||
return Err(InstructionError::InvalidAccountData);
|
||||
}
|
||||
|
||||
let mut cursor = Cursor::new(input);
|
||||
|
||||
let variant = read_u32(&mut cursor)?;
|
||||
|
@ -410,7 +416,13 @@ impl VoteState {
|
|||
// Current. the only difference from V1_14_11 is the addition of a slot-latency to each vote
|
||||
2 => deserialize_vote_state_into(&mut cursor, vote_state, true),
|
||||
_ => Err(InstructionError::InvalidAccountData),
|
||||
}?;
|
||||
|
||||
if cursor.position() > input.len() as u64 {
|
||||
return Err(InstructionError::InvalidAccountData);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn serialize(
|
||||
|
@ -886,7 +898,7 @@ mod tests {
|
|||
|
||||
// variant
|
||||
// provide 4x the minimum struct size in bytes to ensure we typically touch every field
|
||||
let struct_bytes_x4 = std::mem::size_of::<u64>() * 4;
|
||||
let struct_bytes_x4 = std::mem::size_of::<VoteState>() * 4;
|
||||
for _ in 0..1000 {
|
||||
let raw_data: Vec<u8> = (0..struct_bytes_x4).map(|_| rand::random::<u8>()).collect();
|
||||
let mut unstructured = Unstructured::new(&raw_data);
|
||||
|
@ -911,7 +923,7 @@ mod tests {
|
|||
assert_eq!(e, InstructionError::InvalidAccountData);
|
||||
|
||||
// variant
|
||||
let serialized_len_x4 = bincode::serialized_size(&test_vote_state).unwrap() * 4;
|
||||
let serialized_len_x4 = serialized_size(&test_vote_state).unwrap() * 4;
|
||||
let mut rng = rand::thread_rng();
|
||||
for _ in 0..1000 {
|
||||
let raw_data_length = rng.gen_range(1..serialized_len_x4);
|
||||
|
@ -1262,7 +1274,7 @@ mod tests {
|
|||
fn test_vote_state_size_of() {
|
||||
let vote_state = VoteState::get_max_sized_vote_state();
|
||||
let vote_state = VoteStateVersions::new_current(vote_state);
|
||||
let size = bincode::serialized_size(&vote_state).unwrap();
|
||||
let size = serialized_size(&vote_state).unwrap();
|
||||
assert_eq!(VoteState::size_of() as u64, size);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ use {
|
|||
serialize_utils::cursor::*,
|
||||
vote::state::{BlockTimestamp, LandedVote, Lockout, VoteState, MAX_ITEMS},
|
||||
},
|
||||
bincode::serialized_size,
|
||||
std::io::Cursor,
|
||||
};
|
||||
|
||||
|
@ -66,6 +67,25 @@ fn read_prior_voters_into<T: AsRef<[u8]>>(
|
|||
cursor: &mut Cursor<T>,
|
||||
vote_state: &mut VoteState,
|
||||
) -> Result<(), InstructionError> {
|
||||
// record our position at the start of the struct
|
||||
let prior_voters_position = cursor.position();
|
||||
|
||||
// `serialized_size()` must be used over `mem::size_of()` because of alignment
|
||||
let is_empty_position = serialized_size(&vote_state.prior_voters)
|
||||
.ok()
|
||||
.and_then(|v| v.checked_add(prior_voters_position))
|
||||
.and_then(|v| v.checked_sub(1))
|
||||
.ok_or(InstructionError::InvalidAccountData)?;
|
||||
|
||||
// move to the end, to check if we need to parse the data
|
||||
cursor.set_position(is_empty_position);
|
||||
|
||||
// if empty, we already read past the end of this struct and need to do no further work
|
||||
// otherwise we go back to the start and proceed to decode the data
|
||||
let is_empty = read_bool(cursor)?;
|
||||
if !is_empty {
|
||||
cursor.set_position(prior_voters_position);
|
||||
|
||||
let mut encountered_null_voter = false;
|
||||
for i in 0..MAX_ITEMS {
|
||||
let prior_voter = read_pubkey(cursor)?;
|
||||
|
@ -83,16 +103,9 @@ fn read_prior_voters_into<T: AsRef<[u8]>>(
|
|||
}
|
||||
}
|
||||
|
||||
let idx = read_u64(cursor)? as usize;
|
||||
vote_state.prior_voters.idx = idx;
|
||||
|
||||
let is_empty_byte = read_u8(cursor)?;
|
||||
let is_empty = match is_empty_byte {
|
||||
0 => false,
|
||||
1 => true,
|
||||
_ => return Err(InstructionError::InvalidAccountData),
|
||||
};
|
||||
vote_state.prior_voters.is_empty = is_empty;
|
||||
vote_state.prior_voters.idx = read_u64(cursor)? as usize;
|
||||
vote_state.prior_voters.is_empty = read_bool(cursor)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue