Skip to content

Commit

Permalink
TransactionView: Signature Meta
Browse files Browse the repository at this point in the history
  • Loading branch information
apfitzge committed Aug 2, 2024
1 parent 3f3f48f commit 1328862
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
2 changes: 2 additions & 0 deletions transaction-view/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ pub mod bytes;
mod bytes;

pub mod result;
#[allow(dead_code)]
mod signature_meta;
97 changes: 97 additions & 0 deletions transaction-view/src/signature_meta.rs
Original file line number Diff line number Diff line change
@@ -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<Self> {
// 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::<Signature>() + core::mem::size_of::<Pubkey>()))
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::<Signature>(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::<Signature>::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::<Signature>());
}

#[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::<Signature>());
}

#[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());
}
}

0 comments on commit 1328862

Please sign in to comment.