1use crate::prelude::*;
2use bytemuck::{try_cast_slice, try_from_bytes};
3use bytemuck::{Pod, Zeroable};
4use std::cell::Ref;
5use superslice::*;
6
7#[zero_copy(unsafe)]
8#[derive(Default, Debug)]
9#[repr(packed)]
10pub struct AggregatorHistoryRow {
11 pub timestamp: i64,
13 pub value: SwitchboardDecimal,
15}
16unsafe impl Pod for AggregatorHistoryRow {}
17unsafe impl Zeroable for AggregatorHistoryRow {}
18
19pub struct AggregatorHistoryBuffer<'a> {
20 pub insertion_idx: usize,
22 pub rows: Ref<'a, [AggregatorHistoryRow]>,
24}
25
26impl<'a> AggregatorHistoryBuffer<'a> {
27 pub fn new(
33 history_buffer: &'a AccountInfo,
34 ) -> anchor_lang::Result<AggregatorHistoryBuffer<'a>> {
35 let data = history_buffer.try_borrow_data()?;
36
37 let mut disc_bytes = [0u8; 8];
38 disc_bytes.copy_from_slice(&data[..8]);
39 if disc_bytes != AggregatorHistoryBuffer::discriminator() {
40 return Err(SwitchboardError::AccountDiscriminatorMismatch.into());
41 }
42
43 let insertion_idx: u32 = *try_from_bytes::<u32>(&data[8..12]).unwrap();
44 return Ok(Self {
45 insertion_idx: insertion_idx as usize,
46 rows: Ref::map(data, |data| try_cast_slice(&data[12..]).unwrap()),
47 });
48 }
49
50 pub fn lower_bound(&self, timestamp: i64) -> Option<AggregatorHistoryRow> {
56 if self.rows[self.insertion_idx].timestamp == 0 {
57 return None;
58 }
59 let lower = &self.rows[..self.insertion_idx + 1];
60 let lahr = lower.lower_bound_by(|x| {
61 let other: i64 = x.timestamp;
62 other.cmp(×tamp)
63 });
64 if lahr < lower.len() && lower[lahr].timestamp == timestamp {
65 return Some(lower[lahr]);
66 }
67 if lahr != 0 {
68 return Some(lower[lahr - 1]);
69 }
70
71 if self.insertion_idx + 1 < self.rows.len()
72 && self.rows[self.insertion_idx + 1].timestamp != 0
73 {
74 let upper = &self.rows[self.insertion_idx + 1..];
75 let uahr = upper.lower_bound_by(|x| {
76 let other: i64 = x.timestamp;
77 other.cmp(×tamp)
78 });
79 if uahr < upper.len() && upper[uahr].timestamp == timestamp {
80 return Some(upper[uahr]);
81 }
82 if uahr != 0 {
83 return Some(upper[uahr - 1]);
84 }
85 }
86 None
87 }
88}
89
90impl<'a> Discriminator for AggregatorHistoryBuffer<'a> {
91 const DISCRIMINATOR: [u8; 8] = [66, 85, 70, 70, 69, 82, 120, 120];
92}
93
94impl<'a> Owner for AggregatorHistoryBuffer<'a> {
95 fn owner() -> Pubkey {
96 *SWITCHBOARD_PROGRAM_ID
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 const HISTORY_BUFFER_DATA: [u8; 292] = [
123 66, 85, 70, 70, 69, 82, 120, 120, 1, 0, 0, 0, 212, 199, 31, 98, 0, 0, 0, 0, 69, 210, 158,
124 59, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 221, 199, 31, 98, 0, 0, 0, 0, 95,
125 37, 234, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 241, 198, 31, 98, 0, 0, 0, 0,
126 11, 195, 200, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 24, 199, 31, 98, 0, 0, 0,
127 0, 183, 43, 255, 101, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 153, 199, 31, 98, 0,
128 0, 0, 0, 117, 187, 242, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 165, 199, 31,
129 98, 0, 0, 0, 0, 69, 250, 67, 236, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 174,
130 199, 31, 98, 0, 0, 0, 0, 83, 177, 74, 103, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0,
131 183, 199, 31, 98, 0, 0, 0, 0, 113, 186, 62, 0, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0,
132 0, 0, 190, 199, 31, 98, 0, 0, 0, 0, 146, 224, 37, 87, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133 8, 0, 0, 0, 201, 199, 31, 98, 0, 0, 0, 0, 215, 90, 246, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
134 0, 0, 7, 0, 0, 0,
135 ];
136
137 const HISTORY_BUFFER_PUBKEY: Pubkey = Pubkey::new_from_array([
139 188, 221, 119, 59, 130, 153, 226, 148, 95, 158, 33, 63, 106, 233, 240, 46, 242, 141, 150,
140 147, 148, 158, 88, 14, 59, 66, 18, 82, 181, 250, 102, 130,
141 ]);
142
143 #[test]
144 fn test_history_buffer() {
145 let mut history_data = HISTORY_BUFFER_DATA;
146 let mut lamports = 0;
147 let history_account_info = AccountInfo::new(
148 &HISTORY_BUFFER_PUBKEY,
149 false,
150 false,
151 &mut lamports,
152 &mut history_data,
153 &SWITCHBOARD_PROGRAM_ID,
154 false,
155 0,
156 );
157 let history_buffer = AggregatorHistoryBuffer::new(&history_account_info).unwrap();
158
159 match history_buffer.lower_bound(1646249940) {
171 None => panic!("failed to retrieve a value for a valid timestamp"),
172 Some(row) => {
173 let correct_value = SwitchboardDecimal {
174 mantissa: 1006022611525,
175 scale: 10,
176 };
177 if row.value != correct_value {
178 panic!(
179 "failed to retrieve correct value at exact timestamp 1646249940. received: {:?}, expected: {:?}",
180 row.value, correct_value
181 )
182 }
183 }
184 };
185
186 match history_buffer.lower_bound(1646249949) {
188 None => panic!("failed to retrieve a value for a valid timestamp"),
189 Some(row) => {
190 let correct_value = SwitchboardDecimal {
191 mantissa: 1005200735,
192 scale: 7,
193 };
194 if row.value != correct_value {
195 panic!(
196 "failed to retrieve correct value at exact timestamp 1646249940. received: {:?}, expected: {:?}",
197 row.value, correct_value
198 )
199 }
200 }
201 };
202
203 match history_buffer.lower_bound(1646249911) {
205 None => panic!("failed to retrieve a value for a valid timestamp"),
206 Some(row) => {
207 let correct_value = SwitchboardDecimal {
208 mantissa: 1005026458225,
209 scale: 10,
210 };
211 if row.value != correct_value {
212 panic!(
213 "failed to retrieve correct value at exact timestamp 1646249911. received: {:?}, expected: {:?}",
214 row.value, correct_value
215 )
216 }
217 }
218 };
219
220 match history_buffer.lower_bound(1646249929) {
222 None => panic!("failed to retrieve a value for a valid timestamp"),
223 Some(row) => {
224 let correct_value = SwitchboardDecimal {
225 mantissa: 1006000855,
226 scale: 7,
227 };
228 if row.value != correct_value {
229 panic!(
230 "failed to retrieve correct value at exact timestamp 1646249911. received: {:?}, expected: {:?}",
231 row.value, correct_value
232 )
233 }
234 }
235 };
236
237 match history_buffer.lower_bound(1646249912) {
239 None => panic!("failed to retrieve a value for a valid timestamp"),
240 Some(row) => {
241 let correct_value = SwitchboardDecimal {
242 mantissa: 1005026458225,
243 scale: 10,
244 };
245 if row.value != correct_value {
246 panic!("failed to retrieve correct value for timestamp 1646249912. received: {:?}, expected: {:?}",row.value, correct_value)
247 }
248 }
249 };
250
251 match history_buffer.lower_bound(1646249910) {
253 None => panic!("failed to retrieve a value for a valid timestamp"),
254 Some(row) => {
255 let correct_value = SwitchboardDecimal {
256 mantissa: 100517196115,
257 scale: 9,
258 };
259 if row.value != correct_value {
260 panic!(
261 "failed to retrieve correct value for timestamp 1646249910. received: {:?}, expected: {:?}",
262 row.value, correct_value
263 )
264 }
265 }
266 };
267
268 match history_buffer.lower_bound(2646249911) {
270 None => panic!("failed to retrieve a value for a valid timestamp"),
271 Some(row) => {
272 let correct_value = SwitchboardDecimal {
273 mantissa: 1005200735,
274 scale: 7,
275 };
276 if row.value != correct_value {
277 panic!("failed to retrieve correct value for timestamp 2646249911. received: {:?}, expected: {:?}",row.value, correct_value)
278 }
279 }
280 };
281
282 match history_buffer.lower_bound(646249911) {
284 None => (),
285 Some(row) => panic!("retrieved row when no value was expected {:?}", row.value),
286 };
287 }
288}