Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
solana-program: VoteState::deserialize() (#34829)
Browse files Browse the repository at this point in the history
* implement a custom parser for `VoteState` which is usuable in a bpf context
* derive or impl `Arbitrary` for `VoteStateVersions` and its component types, for test builds only
  • Loading branch information
2501babe authored Jan 25, 2024
1 parent b18f738 commit 0c2d9d2
Show file tree
Hide file tree
Showing 11 changed files with 424 additions and 7 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ aquamarine = "0.3.3"
aes-gcm-siv = "0.10.3"
ahash = "0.8.7"
anyhow = "1.0.79"
arbitrary = "1.3.2"
ark-bn254 = "0.4.0"
ark-ec = "0.4.0"
ark-ff = "0.4.0"
Expand Down
1 change: 1 addition & 0 deletions sdk/program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ wasm-bindgen = { workspace = true }
zeroize = { workspace = true, features = ["default", "zeroize_derive"] }

[target.'cfg(not(target_os = "solana"))'.dev-dependencies]
arbitrary = { workspace = true, features = ["derive"] }
solana-logger = { workspace = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
Expand Down
4 changes: 4 additions & 0 deletions sdk/program/src/pubkey.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Solana account addresses.
#![allow(clippy::arithmetic_side_effects)]

#[cfg(test)]
use arbitrary::Arbitrary;
use {
crate::{decode_error::DecodeError, hash::hashv, wasm_bindgen},
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
Expand Down Expand Up @@ -85,6 +88,7 @@ impl From<u64> for PubkeyError {
Zeroable,
)]
#[borsh(crate = "borsh")]
#[cfg_attr(test, derive(Arbitrary))]
pub struct Pubkey(pub(crate) [u8; 32]);

impl crate::sanitize::Sanitize for Pubkey {}
Expand Down
133 changes: 133 additions & 0 deletions sdk/program/src/serialize_utils/cursor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use {
crate::{instruction::InstructionError, pubkey::Pubkey},
std::io::{Cursor, Read},
};

pub(crate) fn read_u8<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<u8, InstructionError> {
let mut buf = [0; 1];
cursor
.read_exact(&mut buf)
.map_err(|_| InstructionError::InvalidAccountData)?;

Ok(buf[0])
}

pub(crate) fn read_u32<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<u32, InstructionError> {
let mut buf = [0; 4];
cursor
.read_exact(&mut buf)
.map_err(|_| InstructionError::InvalidAccountData)?;

Ok(u32::from_le_bytes(buf))
}

pub(crate) fn read_u64<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<u64, InstructionError> {
let mut buf = [0; 8];
cursor
.read_exact(&mut buf)
.map_err(|_| InstructionError::InvalidAccountData)?;

Ok(u64::from_le_bytes(buf))
}

pub(crate) fn read_option_u64<T: AsRef<[u8]>>(
cursor: &mut Cursor<T>,
) -> Result<Option<u64>, InstructionError> {
let variant = read_u8(cursor)?;
match variant {
0 => Ok(None),
1 => read_u64(cursor).map(Some),
_ => Err(InstructionError::InvalidAccountData),
}
}

pub(crate) fn read_i64<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<i64, InstructionError> {
let mut buf = [0; 8];
cursor
.read_exact(&mut buf)
.map_err(|_| InstructionError::InvalidAccountData)?;

Ok(i64::from_le_bytes(buf))
}

pub(crate) fn read_pubkey<T: AsRef<[u8]>>(
cursor: &mut Cursor<T>,
) -> Result<Pubkey, InstructionError> {
let mut buf = [0; 32];
cursor
.read_exact(&mut buf)
.map_err(|_| InstructionError::InvalidAccountData)?;

Ok(Pubkey::from(buf))
}

#[cfg(test)]
mod test {
use {super::*, rand::Rng, std::fmt::Debug};

#[test]
fn test_read_u8() {
for _ in 0..100 {
let test_value = rand::random::<u8>();
test_read(read_u8, test_value);
}
}

#[test]
fn test_read_u32() {
for _ in 0..100 {
let test_value = rand::random::<u32>();
test_read(read_u32, test_value);
}
}

#[test]
fn test_read_u64() {
for _ in 0..100 {
let test_value = rand::random::<u64>();
test_read(read_u64, test_value);
}
}

#[test]
fn test_read_option_u64() {
for _ in 0..100 {
let test_value = rand::random::<Option<u64>>();
test_read(read_option_u64, test_value);
}
}

#[test]
fn test_read_i64() {
for _ in 0..100 {
let test_value = rand::random::<i64>();
test_read(read_i64, test_value);
}
}

#[test]
fn test_read_pubkey() {
for _ in 0..100 {
let mut buf = [0; 32];
rand::thread_rng().fill(&mut buf);
let test_value = Pubkey::from(buf);
test_read(read_pubkey, test_value);
}
}

fn test_read<T: Debug + PartialEq + serde::Serialize + borsh0_10::BorshSerialize>(
reader: fn(&mut Cursor<Vec<u8>>) -> Result<T, InstructionError>,
test_value: T,
) {
let bincode_bytes = bincode::serialize(&test_value).unwrap();
let mut cursor = Cursor::new(bincode_bytes);
let bincode_read = reader(&mut cursor).unwrap();

let borsh_bytes = borsh0_10::to_vec(&test_value).unwrap();
let mut cursor = Cursor::new(borsh_bytes);
let borsh_read = reader(&mut cursor).unwrap();

assert_eq!(test_value, bincode_read);
assert_eq!(test_value, borsh_read);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#![allow(clippy::arithmetic_side_effects)]
use crate::{pubkey::Pubkey, sanitize::SanitizeError};

pub mod cursor;

pub fn append_u16(buf: &mut Vec<u8>, data: u16) {
let start = buf.len();
buf.resize(buf.len() + 2, 0);
Expand Down
3 changes: 3 additions & 0 deletions sdk/program/src/vote/authorized_voters.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#[cfg(test)]
use arbitrary::Arbitrary;
use {
crate::{clock::Epoch, pubkey::Pubkey},
serde_derive::{Deserialize, Serialize},
std::collections::BTreeMap,
};

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
#[cfg_attr(test, derive(Arbitrary))]
pub struct AuthorizedVoters {
authorized_voters: BTreeMap<Epoch, Pubkey>,
}
Expand Down
Loading

0 comments on commit 0c2d9d2

Please sign in to comment.