use crate::sysvar::ed25519_sysvar::Ed25519SignatureOffsets;
use crate::smallvec::{SmallVec, U8Prefix, U16Prefix};
use crate::on_demand::oracle_quote::feed_info::{PackedFeedInfo, PackedQuoteHeader};
use crate::Pubkey;
pub const QUOTE_DISCRIMINATOR: &[u8; 8] = b"SBOracle";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct OracleSignature {
pub offsets: Ed25519SignatureOffsets,
pub pubkey: Pubkey,
pub signature: [u8; 64],
}
impl borsh::BorshSerialize for OracleSignature {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
self.offsets.serialize(writer)?;
writer.write_all(self.pubkey.as_ref())?;
writer.write_all(&self.signature)?;
Ok(())
}
}
impl borsh::BorshDeserialize for OracleSignature {
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
let offsets = Ed25519SignatureOffsets::deserialize_reader(reader)?;
let mut pubkey_bytes = [0u8; 32];
reader.read_exact(&mut pubkey_bytes)?;
let pubkey = Pubkey::new_from_array(pubkey_bytes);
let mut signature = [0u8; 64];
reader.read_exact(&mut signature)?;
Ok(Self {
offsets,
pubkey,
signature,
})
}
}
#[cfg(feature = "anchor")]
impl anchor_lang::AnchorDeserialize for OracleSignature {
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
<Self as borsh::BorshDeserialize>::deserialize_reader(reader)
}
}
#[cfg(feature = "anchor")]
impl anchor_lang::AnchorSerialize for OracleSignature {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
<Self as borsh::BorshSerialize>::serialize(self, writer)
}
}
#[cfg(feature = "idl-build")]
impl anchor_lang::IdlBuild for OracleSignature {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SwitchboardQuote {
pub queue: Pubkey,
pub signatures: SmallVec<OracleSignature, U16Prefix>,
pub quote_header: PackedQuoteHeader,
pub feeds: SmallVec<PackedFeedInfo, U8Prefix>,
pub oracle_idxs: SmallVec<u8, U8Prefix>,
pub slot: u64,
pub version: u8,
pub tail_discriminator: [u8; 4],
}
impl borsh::BorshSerialize for SwitchboardQuote {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_all(self.queue.as_ref())?;
self.signatures.serialize(writer)?;
self.quote_header.serialize(writer)?;
self.feeds.serialize(writer)?;
self.oracle_idxs.serialize(writer)?;
self.slot.serialize(writer)?;
self.version.serialize(writer)?;
self.tail_discriminator.serialize(writer)?;
Ok(())
}
}
impl borsh::BorshDeserialize for SwitchboardQuote {
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
let mut queue_bytes = [0u8; 32];
reader.read_exact(&mut queue_bytes)?;
Ok(Self {
queue: Pubkey::new_from_array(queue_bytes),
signatures: borsh::BorshDeserialize::deserialize_reader(reader)?,
quote_header: borsh::BorshDeserialize::deserialize_reader(reader)?,
feeds: borsh::BorshDeserialize::deserialize_reader(reader)?,
oracle_idxs: borsh::BorshDeserialize::deserialize_reader(reader)?,
slot: borsh::BorshDeserialize::deserialize_reader(reader)?,
version: borsh::BorshDeserialize::deserialize_reader(reader)?,
tail_discriminator: borsh::BorshDeserialize::deserialize_reader(reader)?,
})
}
}
#[cfg(feature = "anchor")]
impl anchor_lang::AnchorDeserialize for SwitchboardQuote {
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
<Self as borsh::BorshDeserialize>::deserialize_reader(reader)
}
}
#[cfg(feature = "anchor")]
impl anchor_lang::AnchorSerialize for SwitchboardQuote {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
<Self as borsh::BorshSerialize>::serialize(self, writer)
}
}
#[cfg(feature = "anchor")]
impl anchor_lang::Discriminator for SwitchboardQuote {
const DISCRIMINATOR: &[u8] = QUOTE_DISCRIMINATOR;
}
#[cfg(feature = "anchor")]
impl anchor_lang::AccountSerialize for SwitchboardQuote {
fn try_serialize<W: std::io::Write>(&self, writer: &mut W) -> anchor_lang::Result<()> {
use anchor_lang::Discriminator;
writer.write_all(Self::DISCRIMINATOR)?;
writer.write_all(self.queue.as_ref())?;
let mut delimited_buf = Vec::new();
borsh::BorshSerialize::serialize(&self.signatures, &mut delimited_buf)
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)?;
borsh::BorshSerialize::serialize(&self.quote_header, &mut delimited_buf)
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)?;
borsh::BorshSerialize::serialize(&self.feeds, &mut delimited_buf)
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)?;
borsh::BorshSerialize::serialize(&self.oracle_idxs, &mut delimited_buf)
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)?;
borsh::BorshSerialize::serialize(&self.slot, &mut delimited_buf)
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)?;
borsh::BorshSerialize::serialize(&self.version, &mut delimited_buf)
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)?;
borsh::BorshSerialize::serialize(&self.tail_discriminator, &mut delimited_buf)
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)?;
let len = delimited_buf.len() as u16;
writer.write_all(&len.to_le_bytes())?;
writer.write_all(&delimited_buf)?;
Ok(())
}
}
#[cfg(feature = "anchor")]
impl anchor_lang::AccountDeserialize for SwitchboardQuote {
fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
use anchor_lang::Discriminator;
use crate::on_demand::oracle_quote::instruction_parser::ParsedEd25519Instruction;
if buf.len() < 40 {
return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorNotFound.into());
}
let given_disc = &buf[..Self::DISCRIMINATOR.len()];
if given_disc != Self::DISCRIMINATOR {
return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorMismatch.into());
}
let queue = Pubkey::new_from_array(buf[8..40].try_into().unwrap());
let data = &buf[40..];
if data.len() < 2 {
return Err(anchor_lang::error::ErrorCode::AccountDidNotDeserialize.into());
}
let len = u16::from_le_bytes([data[0], data[1]]) as usize;
if data.len() < len + 2 {
return Err(anchor_lang::error::ErrorCode::AccountDidNotDeserialize.into());
}
let ed25519_data = &data[2..len + 2];
let parsed = ParsedEd25519Instruction::parse(ed25519_data)
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?;
let signatures = parsed.signatures.into_iter()
.map(|sig| OracleSignature {
offsets: sig.offsets,
pubkey: Pubkey::new_from_array(sig.pubkey),
signature: sig.signature,
})
.collect::<Vec<_>>()
.try_into()
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?;
let feeds = parsed.feeds.into_iter()
.collect::<Vec<_>>()
.try_into()
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?;
let oracle_idxs = parsed.oracle_idxs.into_iter()
.collect::<Vec<_>>()
.try_into()
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?;
Ok(Self {
queue,
signatures,
quote_header: parsed.quote_header,
feeds,
oracle_idxs,
slot: parsed.slot,
version: parsed.version,
tail_discriminator: parsed.discriminator,
})
}
fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
use crate::on_demand::oracle_quote::instruction_parser::ParsedEd25519Instruction;
if buf.len() < 40 {
return Err(anchor_lang::error::ErrorCode::AccountDidNotDeserialize.into());
}
let full_buf = *buf;
*buf = &[];
let queue = Pubkey::new_from_array(full_buf[8..40].try_into().unwrap());
let data = &full_buf[40..];
if data.len() < 2 {
return Err(anchor_lang::error::ErrorCode::AccountDidNotDeserialize.into());
}
let len = u16::from_le_bytes([data[0], data[1]]) as usize;
if data.len() < len + 2 {
return Err(anchor_lang::error::ErrorCode::AccountDidNotDeserialize.into());
}
let ed25519_data = &data[2..len + 2];
let parsed = ParsedEd25519Instruction::parse(ed25519_data)
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?;
let signatures = parsed.signatures.into_iter()
.map(|sig| OracleSignature {
offsets: sig.offsets,
pubkey: Pubkey::new_from_array(sig.pubkey),
signature: sig.signature,
})
.collect::<Vec<_>>()
.try_into()
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?;
let feeds = parsed.feeds.into_iter()
.collect::<Vec<_>>()
.try_into()
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?;
let oracle_idxs = parsed.oracle_idxs.into_iter()
.collect::<Vec<_>>()
.try_into()
.map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?;
Ok(Self {
queue,
signatures,
quote_header: parsed.quote_header,
feeds,
oracle_idxs,
slot: parsed.slot,
version: parsed.version,
tail_discriminator: parsed.discriminator,
})
}
}
#[cfg(feature = "idl-build")]
impl anchor_lang::IdlBuild for SwitchboardQuote {}
impl SwitchboardQuote {
pub const MAX_LEN: usize = 32 + (2 + 8 * 110) + 32 + (1 + 8 * 49) + (1 + 8) + 8 + 1 + 4;
pub const MIN_LEN: usize = 32 + 2 + 32 + 1 + 1 + 8 + 1 + 4;
pub fn feeds_slice(&self) -> &[PackedFeedInfo] {
self.feeds.as_slice()
}
#[cfg(feature = "anchor")]
pub fn get_canonical_key(
queue_key: &anchor_lang::solana_program::pubkey::Pubkey,
feed_ids: &[&[u8; 32]],
program_id: &anchor_lang::solana_program::pubkey::Pubkey,
) -> anchor_lang::solana_program::pubkey::Pubkey {
let mut seeds: Vec<&[u8]> = Vec::with_capacity(feed_ids.len() + 1);
seeds.push(queue_key.as_ref());
for id in feed_ids {
seeds.push(id.as_slice());
}
let (oracle_account, _) =
anchor_lang::solana_program::pubkey::Pubkey::find_program_address(&seeds, program_id);
oracle_account
}
#[cfg(feature = "anchor")]
pub fn canonical_key(
&self,
queue_key: &anchor_lang::solana_program::pubkey::Pubkey,
owner: &anchor_lang::solana_program::pubkey::Pubkey,
) -> anchor_lang::solana_program::pubkey::Pubkey {
let feed_ids: Vec<&[u8; 32]> = self.feeds.iter().map(|feed| &feed.feed_id).collect();
Self::get_canonical_key(queue_key, &feed_ids, owner)
}
}
#[cfg(feature = "anchor")]
impl anchor_lang::Owner for SwitchboardQuote {
fn owner() -> anchor_lang::solana_program::pubkey::Pubkey {
crate::QUOTE_PROGRAM_ID
}
}