agave_transaction_view/
static_account_keys_frame.rs

1use {
2    crate::{
3        bytes::{advance_offset_for_array, read_byte},
4        result::{Result, TransactionViewError},
5    },
6    solana_packet::PACKET_DATA_SIZE,
7    solana_pubkey::Pubkey,
8};
9
10// The packet has a maximum length of 1232 bytes.
11// This means the maximum number of 32 byte keys is 38.
12// 38 as an min-sized encoded u16 is 1 byte.
13// We can simply read this byte, if it's >38 we can return None.
14pub const MAX_STATIC_ACCOUNTS_PER_PACKET: u8 =
15    (PACKET_DATA_SIZE / core::mem::size_of::<Pubkey>()) as u8;
16
17/// Contains metadata about the static account keys in a transaction packet.
18#[derive(Debug, Default)]
19pub(crate) struct StaticAccountKeysFrame {
20    /// The number of static accounts in the transaction.
21    pub(crate) num_static_accounts: u8,
22    /// The offset to the first static account in the transaction.
23    pub(crate) offset: u16,
24}
25
26impl StaticAccountKeysFrame {
27    #[inline(always)]
28    pub(crate) fn try_new(bytes: &[u8], offset: &mut usize) -> Result<Self> {
29        // Max size must not have the MSB set so that it is size 1.
30        const _: () = assert!(MAX_STATIC_ACCOUNTS_PER_PACKET & 0b1000_0000 == 0);
31
32        let num_static_accounts = read_byte(bytes, offset)?;
33        if num_static_accounts == 0 || num_static_accounts > MAX_STATIC_ACCOUNTS_PER_PACKET {
34            return Err(TransactionViewError::ParseError);
35        }
36
37        // We also know that the offset must be less than 3 here, since the
38        // compressed u16 can only use up to 3 bytes, so there is no need to
39        // check if the offset is greater than u16::MAX.
40        let static_accounts_offset = *offset as u16;
41        // Update offset for array of static accounts.
42        advance_offset_for_array::<Pubkey>(bytes, offset, u16::from(num_static_accounts))?;
43
44        Ok(Self {
45            num_static_accounts,
46            offset: static_accounts_offset,
47        })
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use {super::*, solana_short_vec::ShortVec};
54
55    #[test]
56    fn test_zero_accounts() {
57        let bytes = bincode::serialize(&ShortVec(Vec::<Pubkey>::new())).unwrap();
58        let mut offset = 0;
59        assert!(StaticAccountKeysFrame::try_new(&bytes, &mut offset).is_err());
60    }
61
62    #[test]
63    fn test_one_account() {
64        let bytes = bincode::serialize(&ShortVec(vec![Pubkey::default()])).unwrap();
65        let mut offset = 0;
66        let frame = StaticAccountKeysFrame::try_new(&bytes, &mut offset).unwrap();
67        assert_eq!(frame.num_static_accounts, 1);
68        assert_eq!(frame.offset, 1);
69        assert_eq!(offset, 1 + core::mem::size_of::<Pubkey>());
70    }
71
72    #[test]
73    fn test_max_accounts() {
74        let signatures = vec![Pubkey::default(); usize::from(MAX_STATIC_ACCOUNTS_PER_PACKET)];
75        let bytes = bincode::serialize(&ShortVec(signatures)).unwrap();
76        let mut offset = 0;
77        let frame = StaticAccountKeysFrame::try_new(&bytes, &mut offset).unwrap();
78        assert_eq!(frame.num_static_accounts, 38);
79        assert_eq!(frame.offset, 1);
80        assert_eq!(offset, 1 + 38 * core::mem::size_of::<Pubkey>());
81    }
82
83    #[test]
84    fn test_too_many_accounts() {
85        let signatures = vec![Pubkey::default(); usize::from(MAX_STATIC_ACCOUNTS_PER_PACKET) + 1];
86        let bytes = bincode::serialize(&ShortVec(signatures)).unwrap();
87        let mut offset = 0;
88        assert!(StaticAccountKeysFrame::try_new(&bytes, &mut offset).is_err());
89    }
90
91    #[test]
92    fn test_u16_max_accounts() {
93        let signatures = vec![Pubkey::default(); u16::MAX as usize];
94        let bytes = bincode::serialize(&ShortVec(signatures)).unwrap();
95        let mut offset = 0;
96        assert!(StaticAccountKeysFrame::try_new(&bytes, &mut offset).is_err());
97    }
98}