switchboard-utils-packed 0.1.33

Switchboard utility crate
Documentation
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,
    // TODO: change name to 'value'
    pub result: f64,
    pub round_open_slot: u64,
    pub round_open_timestamp: i64,
    pub min_response: f64,
    pub max_response: f64,
    // Rust decimal representation of the current result.
    pub decimal: SwitchboardDecimal,
}

#[derive(Default,Copy,AsBytes,FromBytes,Clone,Debug)]
#[repr(packed)]
pub struct FastRoundResultAccountData {
    pub parent: PubkeyBuffer,
    // TODO: change name to 'round_result'
    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,      // key: &'a Pubkey,
            false,    // is_signer: bool,
            true,     // is_writable: bool,
            lamports, // lamports: &'a mut u64,
            data,     // data: &'a mut [u8],
            owner,    // owner: &'a Pubkey,
            false,    // executable: bool,
            100,      // rent_epoch: Epoch
        )
    }

    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(),
        }
    }

}