switchboard_program_bm/
lib.rs1use quick_protobuf::deserialize_from_slice;
2use solana_program::account_info::AccountInfo;
3use solana_program::program_error::ProgramError;
4
5pub use switchboard_utils_bm::{FastRoundResultAccountData, FastRoundResult, fast_parse_switchboard_result};
6pub use switchboard_protos::protos::aggregator_state::AggregatorState;
7pub use switchboard_protos::protos::aggregator_state::mod_AggregatorState;
8pub use switchboard_protos::protos::aggregator_state::RoundResult;
9pub use switchboard_protos::protos::switchboard_account_types::SwitchboardAccountType;
10use switchboard_protos::protos::vrf::VrfAccountData;
11use bytemuck::{bytes_of_mut, bytes_of};
12
13pub fn is_current_round_valid(aggregator: &AggregatorState) -> Result<bool, ProgramError> {
15 let maybe_round = aggregator.current_round_result.clone();
16 if maybe_round.is_none() {
17 return Ok(false);
18 }
19 let round = maybe_round.unwrap();
20 let configs = aggregator.configs.as_ref().ok_or(ProgramError::InvalidAccountData)?;
21 if round.num_success < configs.min_confirmations {
22 return Ok(false);
23 }
24 Ok(true)
25}
26
27pub fn get_aggregator(switchboard_feed: &AccountInfo) -> Result<AggregatorState, ProgramError> {
32 let state_buffer = switchboard_feed.try_borrow_data()?;
33 if state_buffer.len() == 0 || state_buffer[0] != SwitchboardAccountType::TYPE_AGGREGATOR as u8 {
34 return Err(ProgramError::InvalidAccountData);
35 }
36 let aggregator_state: AggregatorState =
37 deserialize_from_slice(&state_buffer[1..]).map_err(|_| ProgramError::InvalidAccountData)?;
38 Ok(aggregator_state)
39}
40
41pub fn get_aggregator_result(aggregator: &AggregatorState) -> Result<RoundResult, ProgramError> {
43 let mut maybe_round = aggregator.current_round_result.clone();
44 if !is_current_round_valid(&aggregator)? {
45 maybe_round = aggregator.last_round_result.clone();
46 }
47 maybe_round.ok_or(ProgramError::InvalidAccountData)
48}
49
50pub struct BundleAccount<'a> {
51 account_info: &'a AccountInfo<'a>
52}
53
54impl<'a> BundleAccount<'a> {
55 const BUFFER_SIZE: usize = 500;
56
57 pub fn new(account: &'a AccountInfo<'a>) -> Result<Self, ProgramError> {
58 let buf = account.try_borrow_data()?;
59 if buf.len() == 0 || buf[0] != SwitchboardAccountType::TYPE_BUNDLE as u8 {
60 return Err(ProgramError::InvalidAccountData);
61 }
62 Ok(Self{
63 account_info: account
64 })
65 }
66
67 pub fn get_idx(&self, idx: usize) -> Result<FastRoundResultAccountData, ProgramError> {
68 let offset: usize = 1 + (idx * Self::BUFFER_SIZE);
69 let term = offset + std::mem::size_of::<FastRoundResultAccountData>();
70 let buf = self.account_info.try_borrow_data()?;
71 if buf.len() < term {
72 return Err(ProgramError::InvalidArgument);
73 }
74 let mut res = FastRoundResultAccountData {
75 ..Default::default()
76 };
77 let recv = bytes_of_mut(&mut res);
78 recv.copy_from_slice(&buf[offset..term]);
79 if res.result.round_open_slot == 0 {
80 return Err(ProgramError::InvalidArgument);
81 }
82 Ok(res)
83 }
84}
85
86
87
88pub struct VrfAccount<'a> {
89 account_info: &'a AccountInfo<'a>
90}
91
92impl<'a> VrfAccount<'a> {
93
94 pub fn new(account: &'a AccountInfo<'a>) -> Result<Self, ProgramError> {
95 let buf = account.try_borrow_data()?;
96 if buf.len() == 0 || buf[0] != SwitchboardAccountType::TYPE_VRF as u8 {
97 return Err(ProgramError::InvalidAccountData);
98 }
99 Ok(Self{
100 account_info: account
101 })
102 }
103
104 pub fn get_verified_randomness(&self) -> Result<Vec<u8>, ProgramError> {
109 let vrf_state: VrfAccountData =
110 deserialize_from_slice(&self.account_info.try_borrow_data()?[1..])
111 .map_err(|_| ProgramError::InvalidAccountData)?;
112 let value = vrf_state.value.ok_or(ProgramError::InvalidAccountData)?;
113 let min_confirmations = vrf_state.min_proof_confirmations
114 .ok_or(ProgramError::InvalidAccountData)?;
115 let num_confirmations = vrf_state.num_proof_confirmations
116 .ok_or(ProgramError::InvalidAccountData)?;
117 if num_confirmations < min_confirmations {
118 return Err(ProgramError::InvalidAccountData);
119 }
120 Ok(value)
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 pub fn new_account_info<'a>(
129 owner: &'a Pubkey,
130 key: &'a Pubkey,
131 lamports: &'a mut u64,
132 data: &'a mut [u8],
133 ) -> AccountInfo<'a> {
134 AccountInfo::new(
135 key, false, true, lamports, data, owner, false, 100, )
144 }
145
146 pub fn create_aggregator(current_round: RoundResult, last_round: RoundResult) -> AggregatorState {
147 AggregatorState {
148 version: Some(1),
149 configs: Some(mod_AggregatorState::Configs {
150 min_confirmations: Some(10),
151 min_update_delay_seconds: Some(10),
152 locked: Some(false),
153 schedule: None,
154 }),
155 fulfillment_manager_pubkey: Some(Vec::new()),
156 job_definition_pubkeys: Vec::new(),
157 agreement: None,
158 current_round_result: Some(current_round),
159 last_round_result: Some(last_round),
160 parse_optimized_result_address: None,
161 bundle_auth_addresses: Vec::new(),
162 }
163 }
164
165 #[test]
166 fn test_reject_current_on_sucess_count() {
167 let current_round = RoundResult {
168 num_success: Some(2),
169 num_error: Some(5),
170 result: Some(97.0),
171 round_open_slot: Some(1),
172 round_open_timestamp: Some(1),
173 min_response: Some(96.0),
174 max_response: Some(100.0),
175 medians: Vec::new(),
176 };
177 let last_round = RoundResult {
178 num_success: Some(30),
179 num_error: Some(0),
180 result: Some(100.0),
181 round_open_slot: Some(1),
182 round_open_timestamp: Some(1),
183 min_response: Some(100.0),
184 max_response: Some(100.0),
185 medians: Vec::new(),
186 };
187 let aggregator = create_aggregator(current_round.clone(), last_round.clone());
188 assert_eq!(get_aggregator_result(&aggregator).unwrap(), last_round.clone());
189 }
190
191 #[test]
192 fn test_accept_current_on_sucess_count() {
193 let current_round = RoundResult {
194 num_success: Some(20),
195 num_error: Some(5),
196 result: Some(97.0),
197 round_open_slot: Some(1),
198 round_open_timestamp: Some(1),
199 min_response: Some(96.0),
200 max_response: Some(100.0),
201 medians: Vec::new(),
202 };
203 let last_round = RoundResult {
204 num_success: Some(30),
205 num_error: Some(0),
206 result: Some(100.0),
207 round_open_slot: Some(1),
208 round_open_timestamp: Some(1),
209 min_response: Some(100.0),
210 max_response: Some(100.0),
211 medians: Vec::new(),
212 };
213 let aggregator = create_aggregator(current_round.clone(), last_round.clone());
214 assert_eq!(get_aggregator_result(&aggregator).unwrap(), current_round.clone());
215 }
216}