use quick_protobuf::message::MessageWrite;
use quick_protobuf::writer::serialize_into_vec;
use solana_program::pubkey::Pubkey;
use solana_program::sysvar::Sysvar;
use solana_program::sysvar::rent;
use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError};
use std::clone::Clone;
use switchboard_protos::protos::aggregator_state::AggregatorState;
use switchboard_protos::protos::aggregator_state::mod_AggregatorState;
use switchboard_protos::protos::fulfillment_manager::FulfillmentManagerState;
use switchboard_protos::protos::fulfillment_manager::mod_FulfillmentManagerState;
use switchboard_protos::protos::switchboard_account_types::SwitchboardAccountType;
use zerocopy::{AsBytes, FromBytes};
use std::io::Error;
use std::io::Write;
use rust_decimal::prelude::Decimal;
use rust_decimal::prelude::FromPrimitive;
#[derive(Default,Copy,AsBytes,FromBytes,Clone,Debug)]
#[repr(packed)]
pub struct SwitchboardDecimal {
pub mantissa: i128,
pub scale: u64
}
impl SwitchboardDecimal {
pub fn from_f64(input: f64) -> Option<Self> {
let dec = Decimal::from_f64(input);
if dec.is_none() {
return None;
}
let dec = dec.unwrap();
Some(Self {
mantissa: dec.mantissa(),
scale: dec.scale().into(),
})
}
}
pub type PubkeyBuffer = [u8; 32];
#[derive(Default,Copy,AsBytes,FromBytes,Clone,Debug)]
#[repr(packed)]
pub struct FastRoundResult {
pub num_success: i32,
pub num_error: i32,
pub result: f64,
pub round_open_slot: u64,
pub round_open_timestamp: i64,
pub min_response: f64,
pub max_response: f64,
pub decimal: SwitchboardDecimal,
}
#[derive(Default,Copy,AsBytes,FromBytes,Clone,Debug)]
#[repr(packed)]
pub struct FastRoundResultAccountData {
pub parent: PubkeyBuffer,
pub result: FastRoundResult,
}
impl FastRoundResultAccountData {
pub fn serialize<W>(&self, writer: &mut W) -> Result<(), Error> where W: Write {
let buf = self.as_bytes().to_vec();
let mut res: Vec<u8> = vec![
SwitchboardAccountType::TYPE_AGGREGATOR_RESULT_PARSE_OPTIMIZED as u8];
res.extend(buf);
writer.write_all(&res)?;
Ok(())
}
pub fn deserialize(buf: &[u8]) -> Result<Self, std::io::Error> {
if buf.len() == 0 || buf[0] != SwitchboardAccountType::TYPE_AGGREGATOR_RESULT_PARSE_OPTIMIZED as u8 {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid buffer account type"));
}
let buf = &buf[1..];
let mut res = FastRoundResultAccountData {..Default::default()};
let recv = res.as_bytes_mut();
if buf.len() < recv.len() {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid buffer length"));
}
recv.copy_from_slice(&buf[..recv.len()]);
return Ok(res);
}
}
pub fn fast_parse_switchboard_result(bytes: &[u8]) -> FastRoundResultAccountData {
let res = FastRoundResultAccountData::deserialize(bytes).unwrap();
return res;
}
pub fn save_state<T: MessageWrite>(
account: &AccountInfo,
state: &T,
type_indicator: SwitchboardAccountType,
) -> Result<Vec<u8>, ProgramError> {
let mut to = account.try_borrow_mut_data()?;
let mut bytes = serialize_into_vec(state).map_err(|_| ProgramError::InvalidAccountData)?;
let mut res = vec![type_indicator as u8];
res.append(&mut bytes);
to[..res.len()].copy_from_slice(&res);
Ok([].to_vec())
}
pub fn save_state_to_buffer<T: MessageWrite>(
state: &T,
type_indicator: SwitchboardAccountType,
) -> Result<Vec<u8>, ProgramError> {
let bytes = serialize_into_vec(state).map_err(|_| ProgramError::InvalidAccountData)?;
Ok(format_buffer(&bytes, type_indicator))
}
pub fn format_buffer(from: &[u8], type_indicator: SwitchboardAccountType) -> Vec<u8> {
let mut res = vec![type_indicator as u8];
res.extend(from);
res
}
pub fn memcpy(to: &mut [u8], from: &[u8]) {
to[..from.len()].copy_from_slice(&from);
}
pub fn is_account_rent_exempt(
rent_sysvar_account: &AccountInfo,
account: &AccountInfo,
) -> Result<bool, ProgramError> {
let data_len = account.try_data_len()?;
let lamports = account.try_lamports()?;
is_rent_exempt(rent_sysvar_account, lamports, data_len)
}
pub fn is_rent_exempt(
rent_sysvar_account: &AccountInfo,
lamports: u64,
data_len: usize,
) -> Result<bool, ProgramError> {
if *rent_sysvar_account.key != rent::ID {
msg!("Incorrect rent sysvar account.");
return Err(ProgramError::IncorrectProgramId);
}
let rent = rent::Rent::from_account_info(rent_sysvar_account)?;
if rent.is_exempt(lamports, data_len as usize) {
return Ok(true);
}
Ok(false)
}
pub trait ProgramAction {
fn validate(&self) -> Result<(), ProgramError>;
fn actuate(&mut self) -> Result<Vec<u8>, ProgramError>;
}
pub struct AccountType {}
impl AccountType {
pub fn check_account(
account: &AccountInfo,
type_indicator: SwitchboardAccountType,
) -> Result<(), ProgramError> {
let buffer = account.try_borrow_data()?;
AccountType::strip_type_byte(&buffer, type_indicator)?;
Ok(())
}
pub fn check_account_buffer(
buf: &[u8],
type_indicator: SwitchboardAccountType,
) -> Result<(), ProgramError> {
AccountType::strip_type_byte(buf, type_indicator)?;
Ok(())
}
pub fn strip_type_byte<'a>(
buf: &'a [u8],
type_indicator: SwitchboardAccountType,
) -> Result<&'a [u8], ProgramError> {
if buf[0] != type_indicator as u8 {
msg!("Account type {:?} != {:?}", buf[0], type_indicator.clone());
return Err(ProgramError::InvalidAccountData);
}
Ok(&buf[1..])
}
pub fn set_type_byte(buf: &mut [u8], type_indicator: SwitchboardAccountType) -> Result<(), ProgramError> {
let type_indicator_byte = type_indicator as u8;
if buf[0] != SwitchboardAccountType::TYPE_UNINITIALIZED as u8 && buf[0] != type_indicator_byte {
msg!("Invalid account type change");
return Err(ProgramError::AccountAlreadyInitialized);
}
buf[0] = type_indicator_byte;
Ok(())
}
}
pub mod mod_test_utils {
use super::*;
pub fn new_account_info<'a>(
owner: &'a Pubkey,
key: &'a Pubkey,
lamports: &'a mut u64,
data: &'a mut [u8],
) -> AccountInfo<'a> {
AccountInfo::new(
key, false, true, lamports, data, owner, false, 100, )
}
pub fn create_aggregator() -> AggregatorState {
AggregatorState {
version: Some(1),
configs: Some(mod_AggregatorState::Configs {
locked: Some(false),
min_confirmations: Some(0),
min_update_delay_seconds: Some(0),
schedule: None,
}),
fulfillment_manager_pubkey: Some(Vec::new()),
job_definition_pubkeys: Vec::new(),
agreement: None,
current_round_result: None,
last_round_result: None,
parse_optimized_result_address: None,
bundle_auth_addresses: Vec::new(),
}
}
pub fn create_fm() -> FulfillmentManagerState {
FulfillmentManagerState {
version: Some(1),
configs: Some(mod_FulfillmentManagerState::Configs {
heartbeat_auth_required: Some(false),
usage_auth_required: Some(false),
locked: Some(false),
}),
entries: Vec::new(),
}
}
}