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 std::io::Error;
14use std::io::Write;
15use rust_decimal::prelude::Decimal;
16use rust_decimal::prelude::FromPrimitive;
17use bytemuck::{bytes_of_mut, bytes_of};
18use bytemuck::{Pod, Zeroable};
19
20#[derive(Default,Copy,Clone,Debug)]
21#[repr(C)]
22pub struct SwitchboardDecimal {
23 pub mantissa: i128,
24 pub scale: u64
25}
26unsafe impl Pod for SwitchboardDecimal {}
27unsafe impl Zeroable for SwitchboardDecimal {}
28
29impl SwitchboardDecimal {
30 pub fn from_f64(input: f64) -> Option<Self> {
31 let dec = Decimal::from_f64(input);
32 if dec.is_none() {
33 return None;
34 }
35 let dec = dec.unwrap();
36 Some(Self {
37 mantissa: dec.mantissa(),
38 scale: dec.scale().into(),
39 })
40 }
41}
42
43pub type PubkeyBuffer = [u8; 32];
44
45#[derive(Default,Copy,Clone,Debug)]
46#[repr(C)]
47pub struct FastRoundResult {
48 pub num_success: i32,
49 pub num_error: i32,
50 pub result: f64,
52 pub round_open_slot: u64,
53 pub round_open_timestamp: i64,
54 pub min_response: f64,
55 pub max_response: f64,
56 pub decimal: SwitchboardDecimal,
58}
59unsafe impl Pod for FastRoundResult {}
60unsafe impl Zeroable for FastRoundResult {}
61
62#[derive(Default,Copy,Clone,Debug)]
63#[repr(C)]
64pub struct FastRoundResultAccountData {
65 pub parent: PubkeyBuffer,
66 pub result: FastRoundResult,
68}
69unsafe impl Pod for FastRoundResultAccountData {}
70unsafe impl Zeroable for FastRoundResultAccountData {}
71
72impl FastRoundResultAccountData {
73 pub fn serialize<W>(&self, writer: &mut W) -> Result<(), Error> where W: Write {
74 let buf = bytes_of(self).to_vec();
75 let mut res: Vec<u8> = vec![
76 SwitchboardAccountType::TYPE_AGGREGATOR_RESULT_PARSE_OPTIMIZED as u8];
77 res.extend(buf);
78 writer.write_all(&res)?;
79 Ok(())
80 }
81
82 pub fn deserialize(buf: &[u8]) -> Result<Self, std::io::Error> {
83 if buf.len() == 0 || buf[0] != SwitchboardAccountType::TYPE_AGGREGATOR_RESULT_PARSE_OPTIMIZED as u8 {
84 return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid buffer account type"));
85 }
86 let buf = &buf[1..];
87 let mut res = FastRoundResultAccountData {..Default::default()};
88 let recv = bytes_of_mut(&mut res);
89 if buf.len() < recv.len() {
90 return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid buffer length"));
91 }
92 recv.copy_from_slice(&buf[..recv.len()]);
93 return Ok(res);
94 }
95}
96
97pub fn fast_parse_switchboard_result(bytes: &[u8]) -> FastRoundResultAccountData {
98 let res = FastRoundResultAccountData::deserialize(bytes).unwrap();
99 return res;
100}
101
102pub fn save_state<T: MessageWrite>(
103 account: &AccountInfo,
104 state: &T,
105 type_indicator: SwitchboardAccountType,
106) -> Result<Vec<u8>, ProgramError> {
107 let mut to = account.try_borrow_mut_data()?;
108 let mut bytes = serialize_into_vec(state).map_err(|_| ProgramError::InvalidAccountData)?;
109 let mut res = vec![type_indicator as u8];
110 res.append(&mut bytes);
111 to[..res.len()].copy_from_slice(&res);
112 Ok([].to_vec())
113}
114
115pub fn save_state_to_buffer<T: MessageWrite>(
116 state: &T,
117 type_indicator: SwitchboardAccountType,
118) -> Result<Vec<u8>, ProgramError> {
119 let bytes = serialize_into_vec(state).map_err(|_| ProgramError::InvalidAccountData)?;
120 Ok(format_buffer(&bytes, type_indicator))
121}
122
123pub fn format_buffer(from: &[u8], type_indicator: SwitchboardAccountType) -> Vec<u8> {
124 let mut res = vec![type_indicator as u8];
125 res.extend(from);
126 res
127}
128
129pub fn memcpy(to: &mut [u8], from: &[u8]) {
130 to[..from.len()].copy_from_slice(&from);
131}
132
133pub fn is_account_rent_exempt(
134 rent_sysvar_account: &AccountInfo,
135 account: &AccountInfo,
136) -> Result<bool, ProgramError> {
137 let data_len = account.try_data_len()?;
138 let lamports = account.try_lamports()?;
139 is_rent_exempt(rent_sysvar_account, lamports, data_len)
140}
141
142pub fn is_rent_exempt(
143 rent_sysvar_account: &AccountInfo,
144 lamports: u64,
145 data_len: usize,
146) -> Result<bool, ProgramError> {
147 if *rent_sysvar_account.key != rent::ID {
148 msg!("Incorrect rent sysvar account.");
149 return Err(ProgramError::IncorrectProgramId);
150 }
151 let rent = rent::Rent::from_account_info(rent_sysvar_account)?;
152 if rent.is_exempt(lamports, data_len as usize) {
153 return Ok(true);
154 }
155 Ok(false)
156}
157
158pub trait ProgramAction {
159 fn validate(&self) -> Result<(), ProgramError>;
160 fn actuate(&mut self) -> Result<Vec<u8>, ProgramError>;
161}
162
163pub struct AccountType {}
164
165impl AccountType {
166 pub fn check_account(
167 account: &AccountInfo,
168 type_indicator: SwitchboardAccountType,
169 ) -> Result<(), ProgramError> {
170 let buffer = account.try_borrow_data()?;
171 AccountType::strip_type_byte(&buffer, type_indicator)?;
172 Ok(())
173 }
174
175 pub fn check_account_buffer(
176 buf: &[u8],
177 type_indicator: SwitchboardAccountType,
178 ) -> Result<(), ProgramError> {
179 AccountType::strip_type_byte(buf, type_indicator)?;
180 Ok(())
181 }
182
183 pub fn strip_type_byte<'a>(
184 buf: &'a [u8],
185 type_indicator: SwitchboardAccountType,
186 ) -> Result<&'a [u8], ProgramError> {
187 if buf[0] != type_indicator as u8 {
188 msg!("Account type {:?} != {:?}", buf[0], type_indicator.clone());
189 return Err(ProgramError::InvalidAccountData);
190 }
191 Ok(&buf[1..])
192 }
193
194 pub fn set_type_byte(buf: &mut [u8], type_indicator: SwitchboardAccountType) -> Result<(), ProgramError> {
195 let type_indicator_byte = type_indicator as u8;
196 if buf[0] != SwitchboardAccountType::TYPE_UNINITIALIZED as u8 && buf[0] != type_indicator_byte {
197 msg!("Invalid account type change");
198 return Err(ProgramError::AccountAlreadyInitialized);
199 }
200 buf[0] = type_indicator_byte;
201 Ok(())
202 }
203}
204
205pub mod mod_test_utils {
206 use super::*;
207
208 pub fn new_account_info<'a>(
209 owner: &'a Pubkey,
210 key: &'a Pubkey,
211 lamports: &'a mut u64,
212 data: &'a mut [u8],
213 ) -> AccountInfo<'a> {
214 AccountInfo::new(
215 key, false, true, lamports, data, owner, false, 100, )
224 }
225
226 pub fn create_aggregator() -> AggregatorState {
227 AggregatorState {
228 version: Some(1),
229 configs: Some(mod_AggregatorState::Configs {
230 locked: Some(false),
231 min_confirmations: Some(0),
232 min_update_delay_seconds: Some(0),
233 schedule: None,
234 }),
235 fulfillment_manager_pubkey: Some(Vec::new()),
236 job_definition_pubkeys: Vec::new(),
237 agreement: None,
238 current_round_result: None,
239 last_round_result: None,
240 parse_optimized_result_address: None,
241 bundle_auth_addresses: Vec::new(),
242 }
243 }
244
245 pub fn create_fm() -> FulfillmentManagerState {
246 FulfillmentManagerState {
247 version: Some(1),
248 configs: Some(mod_FulfillmentManagerState::Configs {
249 heartbeat_auth_required: Some(false),
250 usage_auth_required: Some(false),
251 locked: Some(false),
252 }),
253 entries: Vec::new(),
254 }
255 }
256
257}