switchbaord_utils/
lib.rs

1use quick_protobuf::message::MessageWrite;
2use quick_protobuf::writer::serialize_into_vec;
3use solana_program::pubkey::Pubkey;
4use solana_program::sysvar::Sysvar;
5use solana_program::sysvar::rent;
6use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError};
7use std::clone::Clone;
8use switchboard_protos::protos::aggregator_state::AggregatorState;
9use switchboard_protos::protos::aggregator_state::mod_AggregatorState;
10use switchboard_protos::protos::fulfillment_manager::FulfillmentManagerState;
11use switchboard_protos::protos::fulfillment_manager::mod_FulfillmentManagerState;
12use switchboard_protos::protos::switchboard_account_types::SwitchboardAccountType;
13use zerocopy::{AsBytes, FromBytes, LayoutVerified};
14
15pub type PubkeyBuffer = [u8; 32];
16
17#[repr(C)]
18#[derive(Default,AsBytes,FromBytes,Clone,Debug)]
19pub struct FastRoundResult {
20    pub num_success: i32,
21    pub num_error: i32,
22    pub result: f64,
23    pub round_open_slot: u64,
24    pub round_open_timestamp: i64,
25    pub min_response: f64,
26    pub max_response: f64,
27}
28
29#[repr(C)]
30#[derive(Default,AsBytes,FromBytes,Clone,Debug)]
31pub struct FastRoundResultAccountData {
32    pub parent: PubkeyBuffer,
33    pub result: FastRoundResult,
34}
35
36pub fn fast_from_bytes<T: Sized + AsBytes + FromBytes + Clone>(bytes: &[u8])
37 -> Result<LayoutVerified<&[u8], T>, ProgramError> {
38    let (res, _) = LayoutVerified::<&[u8], T>::new_from_prefix(bytes)
39        .ok_or(ProgramError::InvalidAccountData)?;
40    return Ok(res);
41}
42
43// pub fn borsh_safe_serialize<T: BorshSerialize>(state: &T, type_indicator: SwitchboardAccountType) -> Vec<u8> {
44    // msg!("borsh serialize");
45    // let mut bytes = state.try_to_vec().unwrap();
46    // let size: usize = bytes.len();
47    // let mut res = vec![type_indicator as u8];
48    // res.append(&mut size.to_le_bytes().to_vec());
49    // res.append(&mut bytes);
50    // return res;
51// }
52//
53// pub fn borsh_safe_deserialize<T: BorshDeserialize>(bytes: &[u8]) -> Result<T, ProgramError> {
54    // let size_buf: &[u8] = &bytes[1..9];
55    // let size = usize::from_le_bytes(size_buf.try_into().unwrap());
56    // let buf = &bytes[9..size + 9];
57    // let res = &buf[..size].to_vec();
58    // T::try_from_slice(res).map_err(|_| ProgramError::InvalidAccountData)
59// }
60
61pub fn save_state<T: MessageWrite>(
62    account: &AccountInfo,
63    state: &T,
64    type_indicator: SwitchboardAccountType,
65) -> Result<Vec<u8>, ProgramError> {
66    let mut to = account.try_borrow_mut_data()?;
67    let mut bytes = serialize_into_vec(state).map_err(|_| ProgramError::InvalidAccountData)?;
68    let mut res = vec![type_indicator as u8];
69    res.append(&mut bytes);
70    to[..res.len()].copy_from_slice(&res);
71    Ok([].to_vec())
72}
73
74pub fn save_state_to_buffer<T: MessageWrite>(
75    state: &T,
76    type_indicator: SwitchboardAccountType,
77) -> Result<Vec<u8>, ProgramError> {
78    let bytes = serialize_into_vec(state).map_err(|_| ProgramError::InvalidAccountData)?;
79    Ok(format_buffer(&bytes, type_indicator))
80}
81
82pub fn format_buffer(from: &[u8], type_indicator: SwitchboardAccountType) -> Vec<u8> {
83    let mut res = vec![type_indicator as u8];
84    res.extend(from);
85    res
86}
87
88pub fn memcpy(to: &mut [u8], from: &[u8]) {
89    to[..from.len()].copy_from_slice(&from);
90}
91
92pub fn is_account_rent_exempt(
93    rent_sysvar_account: &AccountInfo,
94    account: &AccountInfo,
95) -> Result<bool, ProgramError> {
96    let data_len = account.try_data_len()?;
97    let lamports = account.try_lamports()?;
98    is_rent_exempt(rent_sysvar_account, lamports, data_len)
99}
100
101pub fn is_rent_exempt(
102    rent_sysvar_account: &AccountInfo,
103    lamports: u64,
104    data_len: usize,
105) -> Result<bool, ProgramError> {
106    if *rent_sysvar_account.key != rent::ID {
107        msg!("Incorrect rent sysvar account.");
108        return Err(ProgramError::IncorrectProgramId);
109    }
110    let rent = rent::Rent::from_account_info(rent_sysvar_account)?;
111    if rent.is_exempt(lamports, data_len as usize) {
112        return Ok(true);
113    }
114    Ok(false)
115}
116
117pub trait ProgramAction {
118    fn validate(&self) -> Result<(), ProgramError>;
119    fn actuate(&mut self) -> Result<Vec<u8>, ProgramError>;
120}
121
122pub struct AccountType {}
123
124impl AccountType {
125    pub fn check_account(
126        account: &AccountInfo,
127        type_indicator: SwitchboardAccountType,
128    ) -> Result<(), ProgramError> {
129        let buffer = account.try_borrow_data()?;
130        AccountType::strip_type_byte(&buffer, type_indicator)?;
131        Ok(())
132    }
133
134    pub fn check_account_buffer(
135        buf: &[u8],
136        type_indicator: SwitchboardAccountType,
137    ) -> Result<(), ProgramError> {
138        AccountType::strip_type_byte(buf, type_indicator)?;
139        Ok(())
140    }
141
142    pub fn strip_type_byte<'a>(
143        buf: &'a [u8],
144        type_indicator: SwitchboardAccountType,
145    ) -> Result<&'a [u8], ProgramError> {
146        if buf[0] != type_indicator as u8 {
147            msg!("Account type {:?} != {:?}", buf[0], type_indicator.clone());
148            return Err(ProgramError::InvalidAccountData);
149        }
150        Ok(&buf[1..])
151    }
152
153    pub fn set_type_byte(buf: &mut [u8], type_indicator: SwitchboardAccountType) -> Result<(), ProgramError> {
154        let type_indicator_byte = type_indicator as u8;
155        if buf[0] != SwitchboardAccountType::TYPE_UNINITIALIZED as u8 && buf[0] != type_indicator_byte {
156            msg!("Invalid account type change");
157            return Err(ProgramError::AccountAlreadyInitialized);
158        }
159        buf[0] = type_indicator_byte;
160        Ok(())
161    }
162}
163
164pub mod mod_test_utils {
165    use super::*;
166
167    pub fn new_account_info<'a>(
168        owner: &'a Pubkey,
169        key: &'a Pubkey,
170        lamports: &'a mut u64,
171        data: &'a mut [u8],
172    ) -> AccountInfo<'a> {
173        AccountInfo::new(
174            key,      // key: &'a Pubkey,
175            false,    // is_signer: bool,
176            true,     // is_writable: bool,
177            lamports, // lamports: &'a mut u64,
178            data,     // data: &'a mut [u8],
179            owner,    // owner: &'a Pubkey,
180            false,    // executable: bool,
181            100,      // rent_epoch: Epoch
182        )
183    }
184
185    pub fn create_aggregator() -> AggregatorState {
186        AggregatorState {
187            version: Some(1),
188            configs: Some(mod_AggregatorState::Configs {
189                locked: Some(false),
190                min_confirmations: Some(0),
191                min_update_delay_seconds: Some(0),
192                schedule: None,
193            }),
194            fulfillment_manager_pubkey: Some(Vec::new()),
195            job_definition_pubkeys: Vec::new(),
196            agreement: None,
197            current_round_result: None,
198            last_round_result: None,
199            parse_optimized_result_address: None,
200        }
201    }
202
203    pub fn create_fm() -> FulfillmentManagerState {
204        FulfillmentManagerState {
205            version: Some(1),
206            configs: Some(mod_FulfillmentManagerState::Configs {
207                heartbeat_auth_required: Some(false),
208                usage_auth_required: Some(false),
209                locked: Some(false),
210            }),
211            entries: Vec::new(),
212        }
213    }
214
215}