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