#[cfg(feature = "anchor")]
use anchor_lang::solana_program;
use anyhow::{anyhow, bail, Result};
use arrayref::array_ref;
use bytemuck;
use solana_program::{msg, sysvar};
#[repr(C)]
#[derive(bytemuck::Pod, bytemuck::Zeroable, Debug, Clone, Copy)]
pub struct SlotHash {
pub slot: u64,
pub hash: [u8; 32],
}
pub fn find_idx(slot_hashes: &[SlotHash], slot: u64) -> Option<usize> {
slot_hashes.binary_search_by(|x| slot.cmp(&x.slot)).ok()
}
#[derive(Copy, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct SlotHashes;
#[cfg(feature = "anchor")]
impl anchor_lang::solana_program::sysvar::SysvarId for SlotHashes {
fn id() -> solana_program::pubkey::Pubkey {
sysvar::slot_hashes::id().to_bytes().into()
}
fn check_id(id: &solana_program::pubkey::Pubkey) -> bool {
sysvar::slot_hashes::id() == id.to_bytes().into()
}
}
#[cfg(feature = "anchor")]
impl anchor_lang::solana_program::sysvar::Sysvar for SlotHashes {
fn size_of() -> usize {
20_488 }
fn from_account_info(
_account_info: &anchor_lang::prelude::AccountInfo,
) -> Result<Self, solana_program::program_error::ProgramError> {
Ok(Self {})
}
}
impl<'a> SlotHashes {
pub fn get_slothash_from_lower_byte(
slot_hashes: &solana_program::account_info::AccountInfo<'a>,
slot: u8,
) -> Result<SlotHash> {
let (upper_slot, lower_slot) = {
let slots_data = slot_hashes.data.borrow();
let slots: &[u8] = array_ref![slots_data, 8, 20_480];
let slots: &[SlotHash] = bytemuck::cast_slice::<u8, SlotHash>(slots);
let upper_slot = (slots[0].slot & 0xFFFFFFFFFFFFFF00) | slot as u64;
let lower_slot = (slots[slots.len() - 1].slot & 0xFFFFFFFFFFFFFF00) | slot as u64;
(upper_slot, lower_slot)
};
if let Ok(slothash) = Self::get_slothash(slot_hashes, upper_slot) {
return Ok(SlotHash {
slot: upper_slot,
hash: slothash,
});
}
Self::get_slothash(slot_hashes, lower_slot).map(|hash| SlotHash {
slot: lower_slot,
hash,
})
}
pub fn get_slothash(
slot_sysvar: &solana_program::account_info::AccountInfo<'a>,
slot: u64,
) -> Result<[u8; 32]> {
assert!(sysvar::slot_hashes::id() == slot_sysvar.key.to_bytes().into());
let slot_hashes = slot_sysvar;
let slots_data = slot_hashes.data.borrow();
let slots: &[u8] = array_ref![slots_data, 8, 20_480];
let slots: &[SlotHash] = bytemuck::cast_slice::<u8, SlotHash>(slots);
if slot > slots[0].slot {
msg!("Error: Your provided slot is too new. Please use confirmed commitment for your connection and processed for simulation.");
bail!("SwitchboardError::InvalidSlotNumber");
}
let idx = find_idx(&slots[..], slot).ok_or_else(|| anyhow!("InvalidSlotNumber"))?;
let signed_slot = slots[idx];
assert_eq!(signed_slot.slot, slot);
Ok(signed_slot.hash)
}
pub fn parse(data: &'a [u8]) -> &'a [SlotHash] {
bytemuck::cast_slice::<u8, SlotHash>(&data[8..])
}
}