use crate::on_demand::bundle::feed_info::{PackedBundleHeader, PackedFeedInfo};
use anyhow::{bail, Result};
pub const ED25519_PUBKEY_SERIALIZED_SIZE: usize = 32;
pub const ED25519_SIGNATURE_SERIALIZED_SIZE: usize = 64;
pub const ED25519_SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 14;
#[derive(Debug, Copy, Clone)]
pub struct Ed25519SignatureHeader {
pub num_signatures: u8,
pub padding: u8,
}
#[derive(Debug, Copy, Clone)]
pub struct Ed25519SignatureOffsets {
pub signature_offset: u16,
pub signature_instruction_index: u16,
pub public_key_offset: u16,
pub public_key_instruction_index: u16,
pub message_data_offset: u16,
pub message_data_size: u16,
pub message_instruction_index: u16,
}
#[derive(Debug, Copy, Clone)]
pub struct ParsedEd25519SignatureDataRef<'a> {
pub offsets: Ed25519SignatureOffsets,
pub pubkey: *const [u8; ED25519_PUBKEY_SERIALIZED_SIZE],
pub signature: *const [u8; ED25519_SIGNATURE_SERIALIZED_SIZE],
pub message: *const u8,
pub message_len: usize,
pub bundle_header: *const PackedBundleHeader,
pub feed_infos: *const PackedFeedInfo,
pub feed_count: usize,
_phantom: core::marker::PhantomData<&'a ()>,
}
impl<'a> ParsedEd25519SignatureDataRef<'a> {
#[inline(always)]
pub fn default_empty() -> Self {
unsafe { core::mem::zeroed() }
}
#[inline(always)]
pub fn is_valid(&self) -> bool {
!self.pubkey.is_null()
}
#[inline(always)]
pub unsafe fn pubkey(&self) -> &'a [u8; ED25519_PUBKEY_SERIALIZED_SIZE] {
&*self.pubkey
}
#[inline(always)]
pub unsafe fn signature(&self) -> &'a [u8; ED25519_SIGNATURE_SERIALIZED_SIZE] {
&*self.signature
}
#[inline(always)]
pub unsafe fn message(&self) -> &'a [u8] {
core::slice::from_raw_parts(self.message, self.message_len)
}
#[inline(always)]
pub unsafe fn bundle_header(&self) -> &'a PackedBundleHeader {
&*self.bundle_header
}
#[inline(always)]
pub unsafe fn feed_infos(&self) -> &'a [PackedFeedInfo] {
core::slice::from_raw_parts(self.feed_infos, self.feed_count)
}
}
pub struct Ed25519Sysvar;
impl Ed25519Sysvar {
#[inline(always)]
pub fn parse_instruction_zero_copy(
data: &[u8],
) -> Result<([ParsedEd25519SignatureDataRef<'_>; 8], u8, &[u8], u64, u8)> {
let data_len = data.len();
if data_len < core::mem::size_of::<Ed25519SignatureHeader>() {
bail!("Data too short for Ed25519SignatureHeader");
}
let header: &Ed25519SignatureHeader = unsafe { std::mem::transmute(&data[0]) };
let num_signatures = header.num_signatures as usize;
if num_signatures > 8 {
bail!("Too many signatures - maximum 8 supported");
}
if num_signatures == 0 {
bail!("No signatures found in instruction data");
}
if data_len < 13 + num_signatures {
bail!(
"Data too short for oracle indices and metadata: need {} bytes, got {}",
9 + num_signatures,
data_len
);
}
let end_of_message = data_len - num_signatures - 13;
let suffix = &data[end_of_message..];
let oracle_idxs: &[u8] = unsafe { suffix.get_unchecked(..num_signatures) };
let suffix = unsafe { suffix.get_unchecked(num_signatures..) };
let slot: u64 = unsafe {
core::ptr::read_unaligned(&suffix[0] as *const u8 as *const u64)
};
let version: u8 = unsafe { *suffix.get_unchecked(8) };
let message_data = &data[..end_of_message];
let message_data_ptr = message_data.as_ptr();
let mut parsed_sigs_array =
unsafe { core::mem::zeroed::<[ParsedEd25519SignatureDataRef; 8]>() };
let parsed_sigs_ptr = parsed_sigs_array.as_mut_ptr() as *mut ParsedEd25519SignatureDataRef;
unsafe {
let mut offset = 2usize;
let offset_ptr = message_data_ptr.add(offset);
let first_offsets = *(offset_ptr as *const Ed25519SignatureOffsets);
let first_message_offset = first_offsets.message_data_offset as usize;
let first_message_size = first_offsets.message_data_size as usize;
let message = core::slice::from_raw_parts(
message_data_ptr.add(first_message_offset),
first_message_size,
);
if first_message_size < core::mem::size_of::<PackedBundleHeader>() {
bail!("Message too short for bundle header");
}
let shared_header: &PackedBundleHeader = std::mem::transmute(&message[0]);
const HEADER_SIZE: usize = core::mem::size_of::<PackedBundleHeader>();
const FEED_INFO_SIZE: usize = core::mem::size_of::<PackedFeedInfo>();
let remaining_bytes = first_message_size - HEADER_SIZE;
if remaining_bytes % FEED_INFO_SIZE != 0 {
bail!("Invalid message size: remaining bytes not divisible by feed info size, got {}, with feed info size {}", remaining_bytes, FEED_INFO_SIZE);
}
let shared_feed_count = remaining_bytes / FEED_INFO_SIZE;
if shared_feed_count > 8 {
bail!(
"Too many feeds in message: {} feeds but maximum is 8",
shared_feed_count
);
}
let shared_feed_infos = core::slice::from_raw_parts(
message.as_ptr().add(HEADER_SIZE) as *const PackedFeedInfo,
shared_feed_count,
);
let first_signature_offset = first_offsets.signature_offset as usize;
let first_pubkey_offset = first_offsets.public_key_offset as usize;
let first_pubkey = &*(message_data_ptr.add(first_pubkey_offset)
as *const [u8; ED25519_PUBKEY_SERIALIZED_SIZE]);
let first_signature = &*(message_data_ptr.add(first_signature_offset)
as *const [u8; ED25519_SIGNATURE_SERIALIZED_SIZE]);
parsed_sigs_ptr.write(ParsedEd25519SignatureDataRef {
offsets: first_offsets,
pubkey: first_pubkey as *const _,
signature: first_signature as *const _,
message: message_data_ptr.add(first_message_offset),
message_len: first_message_size,
bundle_header: shared_header as *const _,
feed_infos: shared_feed_infos.as_ptr(),
feed_count: shared_feed_count,
_phantom: core::marker::PhantomData,
});
offset += ED25519_SIGNATURE_OFFSETS_SERIALIZED_SIZE;
for i in 1..num_signatures {
let offset_ptr = message_data_ptr.add(offset);
let offsets = *(offset_ptr as *const Ed25519SignatureOffsets);
let signature_offset = offsets.signature_offset as usize;
let pubkey_offset = offsets.public_key_offset as usize;
let message_offset = offsets.message_data_offset as usize;
let message_size = offsets.message_data_size as usize;
if message_offset != first_message_offset || message_size != first_message_size {
bail!("Inconsistent message offsets or sizes");
}
let pubkey = &*(message_data_ptr.add(pubkey_offset)
as *const [u8; ED25519_PUBKEY_SERIALIZED_SIZE]);
let signature = &*(message_data_ptr.add(signature_offset)
as *const [u8; ED25519_SIGNATURE_SERIALIZED_SIZE]);
parsed_sigs_ptr.add(i).write(ParsedEd25519SignatureDataRef {
offsets,
pubkey: pubkey as *const _,
signature: signature as *const _,
message: message_data_ptr.add(message_offset),
message_len: message_size,
bundle_header: shared_header as *const _,
feed_infos: shared_feed_infos.as_ptr(),
feed_count: shared_feed_count,
_phantom: core::marker::PhantomData,
});
offset += ED25519_SIGNATURE_OFFSETS_SERIALIZED_SIZE;
}
Ok((
parsed_sigs_array,
num_signatures as u8,
oracle_idxs,
slot,
version,
))
}
}
#[inline(always)]
pub fn parse_combined_ed25519_instruction_zero_copy(
data: &[u8],
) -> Result<([ParsedEd25519SignatureDataRef<'_>; 8], u8, &[u8], u64, u8)> {
Self::parse_instruction_zero_copy(data)
}
}