From 132886245ebd393c0b6c73ff34466fac5b80bc0c Mon Sep 17 00:00:00 2001 From: Andrew Fitzgerald Date: Fri, 2 Aug 2024 09:43:18 -0500 Subject: [PATCH 1/3] TransactionView: Signature Meta --- transaction-view/src/lib.rs | 2 + transaction-view/src/signature_meta.rs | 97 ++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 transaction-view/src/signature_meta.rs diff --git a/transaction-view/src/lib.rs b/transaction-view/src/lib.rs index a16187f62ccd82..e73eaa0cba1249 100644 --- a/transaction-view/src/lib.rs +++ b/transaction-view/src/lib.rs @@ -7,3 +7,5 @@ pub mod bytes; mod bytes; pub mod result; +#[allow(dead_code)] +mod signature_meta; diff --git a/transaction-view/src/signature_meta.rs b/transaction-view/src/signature_meta.rs new file mode 100644 index 00000000000000..d775513d1abbaf --- /dev/null +++ b/transaction-view/src/signature_meta.rs @@ -0,0 +1,97 @@ +use { + crate::{ + bytes::{offset_array_len, read_byte}, + result::{Result, TransactionParsingError}, + }, + solana_sdk::{packet::PACKET_DATA_SIZE, pubkey::Pubkey, signature::Signature}, +}; + +/// Meta data for accessing transaction-level signatures in a transaction view. +pub(crate) struct SignatureMeta { + /// The number of signatures in the transaction. + pub(crate) num_signatures: u16, + /// Offset to the first signature in the transaction packet. + pub(crate) offset: u16, +} + +impl SignatureMeta { + /// Get the number of signatures and the offset to the first signature in + /// the transaction packet, starting at the given `offset`. + pub(crate) fn try_new(bytes: &[u8], offset: &mut usize) -> Result { + // The packet has a maximum length of 1232 bytes. + // Each signature must be paired with a unique static pubkey, so each + // signature really requires 96 bytes. This means the maximum number of + // signatures in a **valid** transaction packet is 12. + // In our u16 encoding scheme, 12 would be encoded as a single byte. + // Rather than using the u16 decoding, we can simply read the byte and + // verify that the MSB is not set. + const MAX_SIGNATURES_PER_PACKET: u16 = (PACKET_DATA_SIZE + / (core::mem::size_of::() + core::mem::size_of::())) + as u16; + // Maximum number of signatures should be represented by a single byte, + // thus the MSB should not be set. + const _: () = assert!(MAX_SIGNATURES_PER_PACKET & 0b1000_0000 == 0); + + let num_signatures = read_byte(bytes, offset)? as u16; + if num_signatures == 0 || num_signatures > MAX_SIGNATURES_PER_PACKET { + return Err(TransactionParsingError); + } + + let signature_offset = *offset as u16; + offset_array_len::(bytes, offset, num_signatures)?; + + Ok(Self { + num_signatures, + offset: signature_offset, + }) + } +} + +#[cfg(test)] +mod tests { + use {super::*, solana_sdk::short_vec::ShortVec}; + + #[test] + fn test_zero_signatures() { + let bytes = bincode::serialize(&ShortVec(Vec::::new())).unwrap(); + let mut offset = 0; + assert!(SignatureMeta::try_new(&bytes, &mut offset).is_err()); + } + + #[test] + fn test_one_signature() { + let bytes = bincode::serialize(&ShortVec(vec![Signature::default()])).unwrap(); + let mut offset = 0; + let meta = SignatureMeta::try_new(&bytes, &mut offset).unwrap(); + assert_eq!(meta.num_signatures, 1); + assert_eq!(meta.offset, 1); + assert_eq!(offset, 1 + core::mem::size_of::()); + } + + #[test] + fn test_max_signatures() { + let signatures = vec![Signature::default(); 12]; + let bytes = bincode::serialize(&ShortVec(signatures)).unwrap(); + let mut offset = 0; + let meta = SignatureMeta::try_new(&bytes, &mut offset).unwrap(); + assert_eq!(meta.num_signatures, 12); + assert_eq!(meta.offset, 1); + assert_eq!(offset, 1 + 12 * core::mem::size_of::()); + } + + #[test] + fn test_too_many_signatures() { + let signatures = vec![Signature::default(); 13]; + let bytes = bincode::serialize(&ShortVec(signatures)).unwrap(); + let mut offset = 0; + assert!(SignatureMeta::try_new(&bytes, &mut offset).is_err()); + } + + #[test] + fn test_u16_max_signatures() { + let signatures = vec![Signature::default(); u16::MAX as usize]; + let bytes = bincode::serialize(&ShortVec(signatures)).unwrap(); + let mut offset = 0; + assert!(SignatureMeta::try_new(&bytes, &mut offset).is_err()); + } +} From 68a6762edfb57cd384b0e998f764bb59b41739b3 Mon Sep 17 00:00:00 2001 From: Andrew Fitzgerald Date: Mon, 5 Aug 2024 09:18:44 -0500 Subject: [PATCH 2/3] rename --- transaction-view/src/bytes.rs | 20 ++++++++++++-------- transaction-view/src/signature_meta.rs | 4 ++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/transaction-view/src/bytes.rs b/transaction-view/src/bytes.rs index a67d8a2ddd8b35..563acf0e9ae5e0 100644 --- a/transaction-view/src/bytes.rs +++ b/transaction-view/src/bytes.rs @@ -102,7 +102,11 @@ pub fn optimized_read_compressed_u16(bytes: &[u8], offset: &mut usize) -> Result /// 2. The size of `T` is small enough such that a usize will not overflow if /// given the maximum array size (u16::MAX). #[inline(always)] -pub fn offset_array_len(bytes: &[u8], offset: &mut usize, len: u16) -> Result<()> { +pub fn advance_offset_for_array( + bytes: &[u8], + offset: &mut usize, + len: u16, +) -> Result<()> { let array_len_bytes = usize::from(len).wrapping_mul(core::mem::size_of::()); check_remaining(bytes, *offset, array_len_bytes)?; *offset = offset.wrapping_add(array_len_bytes); @@ -116,7 +120,7 @@ pub fn offset_array_len(bytes: &[u8], offset: &mut usize, len: u16) -> /// 1. The current offset is not greater than `bytes.len()`. /// 2. The size of `T` is small enough such that a usize will not overflow. #[inline(always)] -pub fn offset_type(bytes: &[u8], offset: &mut usize) -> Result<()> { +pub fn advance_offset_for_type(bytes: &[u8], offset: &mut usize) -> Result<()> { let type_size = core::mem::size_of::(); check_remaining(bytes, *offset, type_size)?; *offset = offset.wrapping_add(type_size); @@ -267,7 +271,7 @@ mod tests { } #[test] - fn test_offset_array_len() { + fn test_advance_offset_for_array() { #[repr(C)] struct MyStruct { _a: u8, @@ -278,17 +282,17 @@ mod tests { // Test with a buffer that is too short let bytes = [0u8; 1]; let mut offset = 0; - assert!(offset_array_len::(&bytes, &mut offset, 1).is_err()); + assert!(advance_offset_for_array::(&bytes, &mut offset, 1).is_err()); // Test with a buffer that is long enough let bytes = [0u8; 4]; let mut offset = 0; - assert!(offset_array_len::(&bytes, &mut offset, 2).is_ok()); + assert!(advance_offset_for_array::(&bytes, &mut offset, 2).is_ok()); assert_eq!(offset, 4); } #[test] - fn test_offset_type() { + fn test_advance_offset_for_type() { #[repr(C)] struct MyStruct { _a: u8, @@ -299,12 +303,12 @@ mod tests { // Test with a buffer that is too short let bytes = [0u8; 1]; let mut offset = 0; - assert!(offset_type::(&bytes, &mut offset).is_err()); + assert!(advance_offset_for_type::(&bytes, &mut offset).is_err()); // Test with a buffer that is long enough let bytes = [0u8; 4]; let mut offset = 0; - assert!(offset_type::(&bytes, &mut offset).is_ok()); + assert!(advance_offset_for_type::(&bytes, &mut offset).is_ok()); assert_eq!(offset, 2); } } diff --git a/transaction-view/src/signature_meta.rs b/transaction-view/src/signature_meta.rs index d775513d1abbaf..9c3003d633310d 100644 --- a/transaction-view/src/signature_meta.rs +++ b/transaction-view/src/signature_meta.rs @@ -1,6 +1,6 @@ use { crate::{ - bytes::{offset_array_len, read_byte}, + bytes::{advance_offset_for_array, read_byte}, result::{Result, TransactionParsingError}, }, solana_sdk::{packet::PACKET_DATA_SIZE, pubkey::Pubkey, signature::Signature}, @@ -38,7 +38,7 @@ impl SignatureMeta { } let signature_offset = *offset as u16; - offset_array_len::(bytes, offset, num_signatures)?; + advance_offset_for_array::(bytes, offset, num_signatures)?; Ok(Self { num_signatures, From b25b74f5f09db0c6b07e78e23637cc52db3e66d5 Mon Sep 17 00:00:00 2001 From: Andrew Fitzgerald Date: Mon, 5 Aug 2024 09:22:31 -0500 Subject: [PATCH 3/3] add test for non-1 offset --- transaction-view/src/signature_meta.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/transaction-view/src/signature_meta.rs b/transaction-view/src/signature_meta.rs index 9c3003d633310d..9e511068c02147 100644 --- a/transaction-view/src/signature_meta.rs +++ b/transaction-view/src/signature_meta.rs @@ -79,6 +79,17 @@ mod tests { assert_eq!(offset, 1 + 12 * core::mem::size_of::()); } + #[test] + fn test_non_zero_offset() { + let mut bytes = bincode::serialize(&ShortVec(vec![Signature::default()])).unwrap(); + bytes.insert(0, 0); // Insert a byte at the beginning of the packet. + let mut offset = 1; // Start at the second byte. + let meta = SignatureMeta::try_new(&bytes, &mut offset).unwrap(); + assert_eq!(meta.num_signatures, 1); + assert_eq!(meta.offset, 2); + assert_eq!(offset, 2 + core::mem::size_of::()); + } + #[test] fn test_too_many_signatures() { let signatures = vec![Signature::default(); 13];