use {
crate::{
bytes::read_byte,
result::{Result, TransactionViewError},
transaction_version::TransactionVersion,
},
solana_message::MESSAGE_VERSION_PREFIX,
};
#[derive(Debug)]
pub(crate) struct MessageHeaderFrame {
pub(crate) offset: u16,
pub(crate) version: TransactionVersion,
pub(crate) num_required_signatures: u8,
pub(crate) num_readonly_signed_accounts: u8,
pub(crate) num_readonly_unsigned_accounts: u8,
}
impl MessageHeaderFrame {
#[inline(always)]
pub(crate) fn try_new(bytes: &[u8], offset: &mut usize) -> Result<Self> {
let message_offset = *offset as u16;
let message_prefix = read_byte(bytes, offset)?;
let (version, num_required_signatures) = if message_prefix & MESSAGE_VERSION_PREFIX != 0 {
let version = message_prefix & !MESSAGE_VERSION_PREFIX;
match version {
0 => (TransactionVersion::V0, read_byte(bytes, offset)?),
_ => return Err(TransactionViewError::ParseError),
}
} else {
(TransactionVersion::Legacy, message_prefix)
};
let num_readonly_signed_accounts = read_byte(bytes, offset)?;
let num_readonly_unsigned_accounts = read_byte(bytes, offset)?;
Ok(Self {
offset: message_offset,
version,
num_required_signatures,
num_readonly_signed_accounts,
num_readonly_unsigned_accounts,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_invalid_version() {
let bytes = [0b1000_0001];
let mut offset = 0;
assert!(MessageHeaderFrame::try_new(&bytes, &mut offset).is_err());
}
#[test]
fn test_legacy_transaction_missing_header_byte() {
let bytes = [5, 0];
let mut offset = 0;
assert!(MessageHeaderFrame::try_new(&bytes, &mut offset).is_err());
}
#[test]
fn test_legacy_transaction_valid() {
let bytes = [5, 1, 2];
let mut offset = 0;
let header = MessageHeaderFrame::try_new(&bytes, &mut offset).unwrap();
assert!(matches!(header.version, TransactionVersion::Legacy));
assert_eq!(header.num_required_signatures, 5);
assert_eq!(header.num_readonly_signed_accounts, 1);
assert_eq!(header.num_readonly_unsigned_accounts, 2);
}
#[test]
fn test_v0_transaction_missing_header_byte() {
let bytes = [MESSAGE_VERSION_PREFIX, 5, 1];
let mut offset = 0;
assert!(MessageHeaderFrame::try_new(&bytes, &mut offset).is_err());
}
#[test]
fn test_v0_transaction_valid() {
let bytes = [MESSAGE_VERSION_PREFIX, 5, 1, 2];
let mut offset = 0;
let header = MessageHeaderFrame::try_new(&bytes, &mut offset).unwrap();
assert!(matches!(header.version, TransactionVersion::V0));
assert_eq!(header.num_required_signatures, 5);
assert_eq!(header.num_readonly_signed_accounts, 1);
assert_eq!(header.num_readonly_unsigned_accounts, 2);
}
}