switchboard-on-demand 0.5.5

A Rust library to interact with the Switchboard Solana program.
Documentation
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; // 2+2+2+2+2+2+2 = 14 bytes

// const SBOD_DISCRIMINATOR: u32 = u32::from_le_bytes(*b"SBOD");

#[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 {
    /// Ultra-efficient zero-copy ED25519 instruction parsing
    /// Supports variable length messages unlike secp256k1
    /// Returns (signatures, sig_count, oracle_idxs, recent_slot, version)
    #[inline(always)]
    pub fn parse_instruction_zero_copy(
        data: &[u8],
    ) -> Result<([ParsedEd25519SignatureDataRef<'_>; 8], u8, &[u8], u64, u8)> {
        let data_len = data.len();
        // Validate minimum size for header before unsafe cast
        if data_len < core::mem::size_of::<Ed25519SignatureHeader>() {
            bail!("Data too short for Ed25519SignatureHeader");
        }

        // Parse the header (num_signatures + padding byte)
        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");
        }

        // Extract recent_slot and version from the end of instruction data first
        // Check for underflow before subtraction
        if data_len < 13 + num_signatures {
            bail!(
                "Data too short for oracle indices and metadata: need {} bytes, got {}",
                9 + num_signatures,
                data_len
            );
        }
        // Discriminator length is 4, slot is 8, version is 1
        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 {
            // Direct u64 read for 64-bit machines - data already little-endian
            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();

        // Use MaybeUninit to avoid unnecessary initialization
        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; // Skip padding byte after count byte

            // Parse the first signature to get shared message structure
            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;

            // Parse message structure once for all signatures
            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,
            );

            // Process first signature (i=0) outside the loop
            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]);

            // Write first signature to array
            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;

            // Process remaining signatures (i=1 to num_signatures-1)
            for i in 1..num_signatures {
                // Ultra-fast direct access - no endianness conversion needed
                let offset_ptr = message_data_ptr.add(offset);
                let offsets = *(offset_ptr as *const Ed25519SignatureOffsets);

                // Direct access without endianness conversion (assumes little-endian or native format)
                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;

                // Verify all messages are identical
                if message_offset != first_message_offset || message_size != first_message_size {
                    bail!("Inconsistent message offsets or sizes");
                }

                // Zero-copy references - no copying or allocation
                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]);

                // Write directly to final array position - no intermediate copy
                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;
            }

            // Array is now fully initialized, safe to assume_init
            Ok((
                parsed_sigs_array,
                num_signatures as u8,
                oracle_idxs,
                slot,
                version,
            ))
        }
    }

    /// Alias for parse_instruction_zero_copy to maintain compatibility
    #[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)
    }
}