referrerpolicy=no-referrer-when-downgrade

pallet_oracle/
traits.rs

1// This file is part of Substrate.
2
3// Copyright (C) 2020-2025 Acala Foundation.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! This module provides traits for data feeding and provisioning.
19
20use sp_runtime::DispatchResult;
21use sp_std::vec::Vec;
22
23/// A trait for feeding data to a data provider.
24pub trait DataFeeder<Key, Value, AccountId> {
25	/// Feeds a new value for a given key.
26	fn feed_value(who: Option<AccountId>, key: Key, value: Value) -> DispatchResult;
27}
28
29/// A simple trait for providing data.
30pub trait DataProvider<Key, Value> {
31	/// Returns the data for a given key.
32	fn get(key: &Key) -> Option<Value>;
33}
34
35/// An extended `DataProvider` that provides timestamped data.
36pub trait DataProviderExtended<Key, TimestampedValue> {
37	/// Returns a list of all keys and their optional timestamped values.
38	fn get_all_values() -> impl Iterator<Item = (Key, Option<TimestampedValue>)>;
39}
40
41/// Returns the median of a list of values.
42pub fn median<T: Ord + Clone>(mut items: Vec<T>) -> Option<T> {
43	if items.is_empty() {
44		return None;
45	}
46
47	let mid_index = items.len() / 2;
48
49	// Won't panic as `items` ensured not empty.
50	let (_, item, _) = items.select_nth_unstable(mid_index);
51	Some(item.clone())
52}
53
54/// Creates a median data provider from a list of other data providers.
55#[macro_export]
56macro_rules! create_median_value_data_provider {
57	($name:ident, $key:ty, $value:ty, $timestamped_value:ty, [$( $provider:ty ),*]) => {
58		pub struct $name;
59		impl $crate::DataProvider<$key, $value> for $name {
60			fn get(key: &$key) -> Option<$value> {
61				let mut values = vec![];
62				$(
63					if let Some(v) = <$provider as $crate::DataProvider<$key, $value>>::get(&key) {
64						values.push(v);
65					}
66				)*
67				$crate::traits::median(values)
68			}
69		}
70		impl $crate::DataProviderExtended<$key, $timestamped_value> for $name {
71			fn get_all_values() -> impl Iterator<Item = ($key, Option<$timestamped_value>)> {
72				let mut keys = sp_std::collections::btree_set::BTreeSet::new();
73				$(
74					<$provider as $crate::DataProviderExtended<$key, $timestamped_value>>::get_all_values()
75						.into_iter()
76						.for_each(|(k, _)| { keys.insert(k); });
77				)*
78				keys.into_iter().map(|k| (k, Self::get(&k)))
79			}
80		}
81	}
82}
83
84/// Used to combine data from multiple providers.
85pub trait CombineData<Key, TimestampedValue> {
86	/// Combine data provided by operators
87	fn combine_data(
88		key: &Key,
89		values: Vec<TimestampedValue>,
90		prev_value: Option<TimestampedValue>,
91	) -> Option<TimestampedValue>;
92}
93
94/// A handler for new data events.
95#[impl_trait_for_tuples::impl_for_tuples(30)]
96pub trait OnNewData<AccountId, Key, Value> {
97	/// New data is available
98	fn on_new_data(who: &AccountId, key: &Key, value: &Value);
99}
100
101#[cfg(test)]
102mod tests {
103	use super::*;
104	use sp_std::cell::RefCell;
105
106	thread_local! {
107		static MOCK_PRICE_1: RefCell<Option<u8>> = RefCell::new(None);
108		static MOCK_PRICE_2: RefCell<Option<u8>> = RefCell::new(None);
109		static MOCK_PRICE_3: RefCell<Option<u8>> = RefCell::new(None);
110		static MOCK_PRICE_4: RefCell<Option<u8>> = RefCell::new(None);
111	}
112
113	macro_rules! mock_data_provider {
114		($provider:ident, $price:ident) => {
115			pub struct $provider;
116			impl $provider {
117				fn set_price(price: Option<u8>) {
118					$price.with(|v| *v.borrow_mut() = price)
119				}
120			}
121			impl DataProvider<u8, u8> for $provider {
122				fn get(_: &u8) -> Option<u8> {
123					$price.with(|v| *v.borrow())
124				}
125			}
126			impl DataProviderExtended<u8, u8> for $provider {
127				fn get_all_values() -> impl Iterator<Item = (u8, Option<u8>)> {
128					vec![(0, Self::get(&0))].into_iter()
129				}
130			}
131		};
132	}
133
134	mock_data_provider!(Provider1, MOCK_PRICE_1);
135	mock_data_provider!(Provider2, MOCK_PRICE_2);
136	mock_data_provider!(Provider3, MOCK_PRICE_3);
137	mock_data_provider!(Provider4, MOCK_PRICE_4);
138
139	create_median_value_data_provider!(
140		Providers,
141		u8,
142		u8,
143		u8,
144		[Provider1, Provider2, Provider3, Provider4]
145	);
146
147	#[test]
148	fn median_value_data_provider_works() {
149		assert_eq!(<Providers as DataProvider<_, _>>::get(&0), None);
150
151		let data = vec![
152			(vec![None, None, None, Some(1)], Some(1)),
153			(vec![None, None, Some(2), Some(1)], Some(2)),
154			(vec![Some(5), Some(2), None, Some(7)], Some(5)),
155			(vec![Some(5), Some(13), Some(2), Some(7)], Some(7)),
156		];
157
158		for (values, target) in data {
159			Provider1::set_price(values[0]);
160			Provider2::set_price(values[1]);
161			Provider3::set_price(values[2]);
162			Provider4::set_price(values[3]);
163
164			assert_eq!(<Providers as DataProvider<_, _>>::get(&0), target);
165		}
166	}
167}