1use crate::abi::{decode, encode, encode_packed, keccak256, Int, ParamType, Token, Uint, U256};
2use crate::access::AccessControlRegistry;
3use crate::whitelist::Whitelist;
4use crate::{ensure, keccak_packed, median, Bytes, Bytes32, DataPoint, Error, StaticRole, Zero};
5
6const ONE_HOUR_IN_SECONDS: u32 = 3600;
7const FIFTEEN_MINUTES_IN_SECONDS: u32 = 900;
8
9pub trait Storage<T> {
12 fn get(&self, key: &Bytes32) -> Option<T>;
13 fn store(&mut self, key: Bytes32, t: T);
14}
15
16pub trait SignatureManger {
18 fn verify(key: &[u8], message: &[u8], signature: &[u8]) -> bool;
27}
28
29pub trait TimestampChecker {
31 fn current_timestamp(&self) -> u32;
32
33 fn is_valid(&self, timestamp: u32) -> bool {
44 let c = self.current_timestamp();
45 timestamp
46 .checked_add(ONE_HOUR_IN_SECONDS)
47 .expect("Invalid timestamp")
48 > c
49 && timestamp < c + FIFTEEN_MINUTES_IN_SECONDS
50 }
51}
52
53pub fn read_with_data_point_id<
64 D: Storage<DataPoint>,
65 A: AccessControlRegistry,
66 W: Whitelist<Address = A::Address>,
67>(
68 datapoint_id: &Bytes32,
69 msg_sender: &A::Address,
70 datapoint_storage: &D,
71 access: &A,
72 whitelist: &W,
73) -> Result<(Int, u32), Error> {
74 ensure!(
75 reader_can_read_data_point(datapoint_id, msg_sender, access, whitelist),
76 Error::AccessDenied
77 )?;
78 let data_point = datapoint_storage
79 .get(datapoint_id)
80 .ok_or(Error::BeaconDataNotFound)?;
81 Ok((data_point.value, data_point.timestamp))
82}
83
84pub fn read_with_name<
98 D: Storage<DataPoint>,
99 H: Storage<Bytes32>,
100 A: AccessControlRegistry,
101 W: Whitelist<Address = A::Address>,
102>(
103 name: Bytes32,
104 msg_sender: &A::Address,
105 datapoint_storage: &D,
106 name_storage: &H,
107 access: &A,
108 whitelist: &W,
109) -> Result<(Int, u32), Error> {
110 let name_hash = keccak_packed(&[Token::FixedBytes(name.to_vec())]);
111 ensure!(
112 reader_can_read_data_point(&name_hash, msg_sender, access, whitelist),
113 Error::AccessDenied
114 )?;
115 let key = name_storage
116 .get(&name_hash)
117 .ok_or(Error::NameHashNotFound)?;
118 let data_point = datapoint_storage
119 .get(&key)
120 .ok_or(Error::BeaconDataNotFound)?;
121 Ok((data_point.value, data_point.timestamp))
122}
123
124pub fn reader_can_read_data_point<A: AccessControlRegistry, W: Whitelist<Address = A::Address>>(
133 data_point_id: &Bytes32,
134 reader: &A::Address,
135 access: &A,
136 whitelist: &W,
137) -> bool {
138 let role = access.find_static_role(StaticRole::UnlimitedReaderRole);
139 reader.is_zero()
140 || whitelist.user_is_whitelisted(data_point_id, reader)
141 || access.has_role(&role, reader)
142}
143
144pub fn update_dapi_with_beacons<D: Storage<DataPoint>>(
151 d: &mut D,
152 beacon_ids: &[Bytes32],
153) -> Result<Bytes32, Error> {
154 let beacon_count = beacon_ids.len();
155 ensure!(beacon_count > 1, Error::LessThanTwoBeacons)?;
156
157 let mut values = Vec::with_capacity(beacon_count);
158 let mut accumulated_timestamp = U256::from(0);
159
160 for beacon_id in beacon_ids {
161 let data_point = d.get(beacon_id).ok_or(Error::BeaconDataNotFound)?;
162 values.push(data_point.value);
163 accumulated_timestamp += U256::from(data_point.timestamp);
164 }
165
166 let dapi_id = derive_dapi_id(beacon_ids);
167 let dapi_datapoint = d.get(&dapi_id).ok_or(Error::BeaconDataNotFound)?;
168
169 let updated_timestamp = (accumulated_timestamp / beacon_count).as_u32();
170 ensure!(
171 updated_timestamp >= dapi_datapoint.timestamp,
172 Error::UpdatedValueOutdated
173 )?;
174 let updated_value = median(&values);
175 let datapoint = DataPoint::new(updated_value, updated_timestamp);
176
177 d.store(dapi_id, datapoint);
178 Ok(dapi_id)
179}
180
181#[allow(clippy::too_many_arguments)]
196pub fn update_dapi_with_signed_data<
197 D: Storage<DataPoint>,
198 S: SignatureManger,
199 T: TimestampChecker,
200>(
201 datapoint_storage: &mut D,
202 timestamp_checker: &T,
203 airnodes: Vec<Bytes>,
204 template_ids: Vec<[u8; 32]>,
205 timestamps: Vec<[u8; 32]>,
206 data: Vec<Bytes>,
207 signatures: Vec<Bytes>,
208) -> Result<Bytes32, Error> {
209 let beacon_count = template_ids.len();
210
211 ensure!(
212 beacon_count == template_ids.len()
213 && beacon_count == timestamps.len()
214 && beacon_count == data.len()
215 && beacon_count == signatures.len(),
216 Error::ParameterLengthMismatch
217 )?;
218
219 ensure!(beacon_count > 1, Error::LessThanTwoBeacons)?;
220
221 let mut beacon_ids = Vec::with_capacity(beacon_count);
222 let mut values = Vec::with_capacity(beacon_count);
223 let mut accumulated_timestamp = U256::from(0);
224
225 for ind in 0..beacon_count {
226 if !signatures[ind].is_empty() {
227 let timestamp = U256::from_big_endian(×tamps[ind]);
228 let timestamp_u32 = timestamp.as_u32();
229 ensure!(
230 timestamp_checker.is_valid(timestamp_u32),
231 Error::InvalidTimestamp
232 )?;
233
234 let message = keccak_packed(&[
235 Token::FixedBytes(template_ids[ind].clone().to_vec()),
236 Token::Uint(timestamp),
237 Token::Bytes(data[ind].clone()),
238 ]);
239 ensure!(
240 S::verify(&airnodes[ind], &message, &signatures[ind]),
241 Error::InvalidSignature
242 )?;
243
244 values.push(decode_fulfillment_data(&data[ind])?);
245
246 accumulated_timestamp += timestamp;
249 beacon_ids.push(derive_beacon_id(
250 airnodes[ind].clone(),
251 template_ids[ind],
252 ));
253 } else {
254 let beacon_id = derive_beacon_id(airnodes[ind].clone(), template_ids[ind]);
255 let data_point = datapoint_storage
256 .get(&beacon_id)
257 .ok_or(Error::BeaconDataNotFound)?;
258 values.push(data_point.value);
259 accumulated_timestamp += U256::from(data_point.timestamp);
260 beacon_ids.push(beacon_id);
261 }
262 }
263 let dapi_id = derive_dapi_id(&beacon_ids);
264 let updated_timestamp = (accumulated_timestamp / beacon_count).as_u32();
265 let dapi_datapoint = datapoint_storage
266 .get(&dapi_id)
267 .ok_or(Error::BeaconDataNotFound)?;
268 ensure!(
269 updated_timestamp >= dapi_datapoint.timestamp,
270 Error::UpdatedValueOutdated
271 )?;
272 let updated_value = median(&values);
273 let datapoint = DataPoint::new(updated_value, updated_timestamp);
274 datapoint_storage.store(dapi_id, datapoint);
275 Ok(dapi_id)
276}
277
278pub fn set_name<D: Storage<Bytes32>, A: AccessControlRegistry>(
292 name: Bytes32,
293 datapoint_id: Bytes32,
294 msg_sender: &A::Address,
295 access: &A,
296 storage: &mut D,
297) -> Result<(), Error> {
298 ensure!(name != Bytes32::default(), Error::InvalidData)?;
299 ensure!(datapoint_id != Bytes32::default(), Error::InvalidData)?;
300 let role = access.find_static_role(StaticRole::NameSetterRole);
301 ensure!(access.has_role(&role, msg_sender), Error::AccessDenied)?;
302
303 storage.store(
304 keccak_packed(&[Token::FixedBytes(name.to_vec())]),
305 datapoint_id,
306 );
307
308 Ok(())
309}
310
311pub fn derive_beacon_id(airnode: Bytes, template_id: Bytes32) -> Bytes32 {
319 ensure!(not_zero(&airnode), Error::AirnodeIdZero).unwrap();
320 ensure!(not_zero(&template_id), Error::TemplateIdZero).unwrap();
321 let (encoded, _) = encode_packed(&[
322 Token::Bytes(airnode),
323 Token::FixedBytes(template_id.to_vec()),
324 ]);
325 keccak256(&encoded)
326}
327
328pub fn derive_dapi_id(beacon_ids: &[Bytes32]) -> Bytes32 {
336 let tokens: Vec<Token> = beacon_ids
337 .iter()
338 .map(|b| Token::FixedBytes(b.to_vec()))
339 .collect();
340 let encoded = encode(&tokens);
341 keccak256(&encoded)
342}
343
344pub fn decode_fulfillment_data(data: &Bytes) -> Result<Int, Error> {
351 ensure!(data.len() == 32, Error::InvalidDataLength)?;
352
353 let tokens = decode(&[ParamType::Int(0)], data)?;
354 ensure!(tokens.len() == 1, Error::InvalidDataLength)?;
355
356 if let Token::Int(i) = tokens[0] {
357 Ok(i)
358 } else {
359 Err(Error::InvalidDataType)
360 }
361}
362
363pub fn process_beacon_update<D: Storage<DataPoint>>(
373 storage: &mut D,
374 beacon_id: Bytes32,
375 timestamp: Uint,
376 data: Bytes,
377) -> Result<(), Error> {
378 let updated_beacon_value = decode_fulfillment_data(&data)?;
379
380 let beacon = storage.get(&beacon_id).ok_or(Error::BeaconDataNotFound)?;
381 ensure!(
382 timestamp.as_u32() > beacon.timestamp,
383 Error::FulfillmentOlderThanBeacon
384 )?;
385
386 let datapoint = DataPoint::new(updated_beacon_value, timestamp.as_u32());
390 storage.store(beacon_id, datapoint);
391
392 Ok(())
393}
394
395fn not_zero(bytes: &[u8]) -> bool {
396 let mut count = 0;
397 for i in bytes {
398 if *i == 0u8 { count += 1; }
399 }
400 count != bytes.len()
401}
402
403#[cfg(test)]
404mod tests {
405 use crate::beacon::not_zero;
406 use crate::derive_beacon_id;
407
408 #[test]
409 fn not_zero_works() {
410 assert!(!not_zero(&[0;12]));
411 let mut v = [0;12];
412 v[2] = 1;
413 assert!(not_zero(&v));
414 }
415
416 #[test]
417 fn encode_packed_works() {
418 let raw_template_id =
419 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
420 .unwrap();
421 let airnode =
422 hex::decode("1d73899cc9fc3ad06a2c7f5bf26c8a4a76b42de905cb9b6ae96390355441a0ca")
423 .unwrap();
424 let mut template_id = [0; 32];
425 template_id.copy_from_slice(&raw_template_id);
426 let beacon_id = derive_beacon_id(airnode, template_id);
427 assert_eq!(
428 hex::encode(beacon_id),
429 "ad1b5c75a8b8e0d7dbc56c1e28aee9fabe285ad8fb61a256ddabd4523bfb284a"
430 );
431 }
432}