From 94e5fd0c63bf8e7253956983db1a08bec31da007 Mon Sep 17 00:00:00 2001 From: Andrew Fitzgerald Date: Wed, 7 Aug 2024 13:07:52 -0500 Subject: [PATCH] TransactionView: Static Account Keys Meta (#2410) --- transaction-view/src/lib.rs | 2 + .../src/static_account_keys_meta.rs | 95 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 transaction-view/src/static_account_keys_meta.rs diff --git a/transaction-view/src/lib.rs b/transaction-view/src/lib.rs index 41870f9d690d91..aa16270fb7313d 100644 --- a/transaction-view/src/lib.rs +++ b/transaction-view/src/lib.rs @@ -11,3 +11,5 @@ mod message_header_meta; pub mod result; #[allow(dead_code)] mod signature_meta; +#[allow(dead_code)] +mod static_account_keys_meta; diff --git a/transaction-view/src/static_account_keys_meta.rs b/transaction-view/src/static_account_keys_meta.rs new file mode 100644 index 00000000000000..46bf95c15b5e58 --- /dev/null +++ b/transaction-view/src/static_account_keys_meta.rs @@ -0,0 +1,95 @@ +use { + crate::{ + bytes::{advance_offset_for_array, read_byte}, + result::{Result, TransactionParsingError}, + }, + solana_sdk::{packet::PACKET_DATA_SIZE, pubkey::Pubkey}, +}; + +/// Contains meta-data about the static account keys in a transaction packet. +#[derive(Default)] +pub struct StaticAccountKeysMeta { + /// The number of static accounts in the transaction. + pub(crate) num_static_accounts: u16, + /// The offset to the first static account in the transaction. + pub(crate) offset: u16, +} + +impl StaticAccountKeysMeta { + pub fn try_new(bytes: &[u8], offset: &mut usize) -> Result { + // The packet has a maximum length of 1232 bytes. + // This means the maximum number of 32 byte keys is 38. + // 38 as an min-sized encoded u16 is 1 byte. + // We can simply read this byte, if it's >38 we can return None. + const MAX_STATIC_ACCOUNTS_PER_PACKET: u16 = + (PACKET_DATA_SIZE / core::mem::size_of::()) as u16; + // Max size must not have the MSB set so that it is size 1. + const _: () = assert!(MAX_STATIC_ACCOUNTS_PER_PACKET & 0b1000_0000 == 0); + + let num_static_accounts = read_byte(bytes, offset)? as u16; + if num_static_accounts == 0 || num_static_accounts > MAX_STATIC_ACCOUNTS_PER_PACKET { + return Err(TransactionParsingError); + } + + // We also know that the offset must be less than 3 here, since the + // compressed u16 can only use up to 3 bytes, so there is no need to + // check if the offset is greater than u16::MAX. + let static_accounts_offset = *offset as u16; + // Update offset for array of static accounts. + advance_offset_for_array::(bytes, offset, num_static_accounts)?; + + Ok(Self { + num_static_accounts, + offset: static_accounts_offset, + }) + } +} + +#[cfg(test)] +mod tests { + use {super::*, solana_sdk::short_vec::ShortVec}; + + #[test] + fn test_zero_accounts() { + let bytes = bincode::serialize(&ShortVec(Vec::::new())).unwrap(); + let mut offset = 0; + assert!(StaticAccountKeysMeta::try_new(&bytes, &mut offset).is_err()); + } + + #[test] + fn test_one_account() { + let bytes = bincode::serialize(&ShortVec(vec![Pubkey::default()])).unwrap(); + let mut offset = 0; + let meta = StaticAccountKeysMeta::try_new(&bytes, &mut offset).unwrap(); + assert_eq!(meta.num_static_accounts, 1); + assert_eq!(meta.offset, 1); + assert_eq!(offset, 1 + core::mem::size_of::()); + } + + #[test] + fn test_max_accounts() { + let signatures = vec![Pubkey::default(); 38]; + let bytes = bincode::serialize(&ShortVec(signatures)).unwrap(); + let mut offset = 0; + let meta = StaticAccountKeysMeta::try_new(&bytes, &mut offset).unwrap(); + assert_eq!(meta.num_static_accounts, 38); + assert_eq!(meta.offset, 1); + assert_eq!(offset, 1 + 38 * core::mem::size_of::()); + } + + #[test] + fn test_too_many_accounts() { + let signatures = vec![Pubkey::default(); 39]; + let bytes = bincode::serialize(&ShortVec(signatures)).unwrap(); + let mut offset = 0; + assert!(StaticAccountKeysMeta::try_new(&bytes, &mut offset).is_err()); + } + + #[test] + fn test_u16_max_accounts() { + let signatures = vec![Pubkey::default(); u16::MAX as usize]; + let bytes = bincode::serialize(&ShortVec(signatures)).unwrap(); + let mut offset = 0; + assert!(StaticAccountKeysMeta::try_new(&bytes, &mut offset).is_err()); + } +}