Skip to content

Commit

Permalink
Add support for generic oracle in pallets (integritee-network#109)
Browse files Browse the repository at this point in the history
* Add support for generic oracle in pallets

* Fix clippy warnings and rustfmt

* Vs code merging error removing merge headers

* Fixing benchmarks

* adding vec

* Cargo fmt

* updating toolchain
  • Loading branch information
coax1d authored Nov 29, 2022
1 parent ee969d7 commit ed263bc
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 84 deletions.
4 changes: 4 additions & 0 deletions primitives/teeracle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
use common_primitives::PalletString;
use substrate_fixed::types::U32F32;

pub const MAX_ORACLE_DATA_NAME_LEN: usize = 40;

pub type ExchangeRate = U32F32;
pub type TradingPairString = PalletString;
pub type MarketDataSourceString = PalletString;
pub type OracleDataName = PalletString;
pub type DataSource = PalletString;
44 changes: 34 additions & 10 deletions teeracle/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@

use super::*;

use crate::Pallet as Exchange;
use crate::Pallet as Teeracle;
use frame_benchmarking::benchmarks;
use frame_system::RawOrigin;
use pallet_teerex::Pallet as Teerex;
use sp_runtime::traits::CheckedConversion;
use teeracle_primitives::{MarketDataSourceString, TradingPairString};
use sp_std::{borrow::ToOwned, prelude::*};
use teeracle_primitives::{DataSource, OracleDataName, TradingPairString};

use test_utils::{
get_signer,
ias::{consts::*, setups::*},
Expand All @@ -46,7 +48,7 @@ benchmarks! {
let signer: T::AccountId = get_signer(TEST4_SETUP.signer_pub);
let trading_pair: TradingPairString = "DOT/USD".into();
let rate = U32F32::from_num(43.65);
let data_source: MarketDataSourceString = "https://api.coingecko.com".into();
let data_source: DataSource = "https://api.coingecko.com".into();
// simply register the enclave before to make sure it already
// exists when running the benchmark
Teerex::<T>::register_enclave(
Expand All @@ -55,31 +57,53 @@ benchmarks! {
URL.to_vec()
).unwrap();
let mrenclave = Teerex::<T>::enclave(1).unwrap().mr_enclave;
Exchange::<T>::add_to_whitelist(RawOrigin::Root.into(), data_source.clone(), mrenclave).unwrap();
Teeracle::<T>::add_to_whitelist(RawOrigin::Root.into(), data_source.clone(), mrenclave).unwrap();

}: _(RawOrigin::Signed(signer), data_source.clone(), trading_pair.clone(), Some(rate))
verify {
assert_eq!(Exchange::<T>::exchange_rate(trading_pair, data_source), U32F32::from_num(43.65));
assert_eq!(Teeracle::<T>::exchange_rate(trading_pair, data_source), U32F32::from_num(43.65));
}

update_oracle {
ensure_not_skipping_ra_check();
timestamp::Pallet::<T>::set_timestamp(TEST4_SETUP.timestamp.checked_into().unwrap());
let signer: T::AccountId = get_signer(TEST4_SETUP.signer_pub);
let oracle_name = OracleDataName::from("Test_Oracle_Name");
let data_source = DataSource::from("Test_Source_Name");
let oracle_blob: crate::OracleDataBlob<T> =
vec![1].try_into().expect("Can Convert to OracleDataBlob<T>; QED");
// simply register the enclave before to make sure it already
// exists when running the benchmark
Teerex::<T>::register_enclave(
RawOrigin::Signed(signer.clone()).into(),
TEST4_SETUP.cert.to_vec(),
URL.to_vec()
).unwrap();
let mrenclave = Teerex::<T>::enclave(1).unwrap().mr_enclave;
Teeracle::<T>::add_to_whitelist(RawOrigin::Root.into(), data_source.clone(), mrenclave).unwrap();
}: _(RawOrigin::Signed(signer), oracle_name.clone(), data_source.clone(), oracle_blob.clone())
verify {
assert_eq!(Teeracle::<T>::oracle_data(oracle_name, data_source), oracle_blob);
}

add_to_whitelist {
let mrenclave = TEST4_MRENCLAVE;
let data_source: MarketDataSourceString = "https://api.coingecko.com".into();
let data_source: DataSource = "https://api.coingecko.com".into();

}: _(RawOrigin::Root, data_source.clone(), mrenclave)
verify {
assert_eq!(Exchange::<T>::whitelist(data_source).len(), 1, "mrenclave not added to whitelist")
assert_eq!(Teeracle::<T>::whitelist(data_source).len(), 1, "mrenclave not added to whitelist")
}

remove_from_whitelist {
let mrenclave = TEST4_MRENCLAVE;
let data_source: MarketDataSourceString = "https://api.coingecko.com".into();
let data_source: DataSource = "https://api.coingecko.com".into();

Exchange::<T>::add_to_whitelist(RawOrigin::Root.into(), data_source.clone(), mrenclave).unwrap();
Teeracle::<T>::add_to_whitelist(RawOrigin::Root.into(), data_source.clone(), mrenclave).unwrap();

}: _(RawOrigin::Root, data_source.clone(), mrenclave)
verify {
assert_eq!(Exchange::<T>::whitelist(data_source).len(), 0, "mrenclave not removed from whitelist")
assert_eq!(Teeracle::<T>::whitelist(data_source).len(), 0, "mrenclave not removed from whitelist")
}
}

Expand Down
81 changes: 67 additions & 14 deletions teeracle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,21 @@
pub use crate::weights::WeightInfo;
pub use pallet::*;
pub use substrate_fixed::types::U32F32;
use teeracle_primitives::MarketDataSourceString;
use teeracle_primitives::{DataSource, MAX_ORACLE_DATA_NAME_LEN};

const MAX_TRADING_PAIR_LEN: usize = 11;
const MAX_SOURCE_LEN: usize = 40;

#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::{pallet_prelude::*, WeakBoundedVec};
use frame_support::{pallet_prelude::*, BoundedVec, WeakBoundedVec};
use frame_system::pallet_prelude::*;
use sp_std::prelude::*;
use teeracle_primitives::*;

pub type OracleDataBlob<T> = BoundedVec<u8, <T as Config>::MaxOracleBlobLen>;

#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::without_storage_info]
Expand All @@ -59,6 +61,9 @@ pub mod pallet {
/// Max number of whitelisted oracle's releases allowed
#[pallet::constant]
type MaxWhitelistedReleases: Get<u32>;

#[pallet::constant]
type MaxOracleBlobLen: Get<u32>;
}

/// Exchange rates chain's cryptocurrency/currency (trading pair) from different sources
Expand All @@ -69,18 +74,30 @@ pub mod pallet {
Blake2_128Concat,
TradingPairString,
Blake2_128Concat,
MarketDataSourceString,
DataSource,
ExchangeRate,
ValueQuery,
>;

#[pallet::storage]
#[pallet::getter(fn oracle_data)]
pub(super) type OracleData<T> = StorageDoubleMap<
_,
Blake2_128Concat,
OracleDataName,
Blake2_128Concat,
DataSource,
OracleDataBlob<T>,
ValueQuery,
>;

/// whitelist of trusted oracle's releases for different data sources
#[pallet::storage]
#[pallet::getter(fn whitelist)]
pub(super) type Whitelists<T: Config> = StorageMap<
_,
Blake2_128Concat,
MarketDataSourceString,
DataSource,
WeakBoundedVec<[u8; 32], T::MaxWhitelistedReleases>,
ValueQuery,
>;
Expand All @@ -92,10 +109,11 @@ pub mod pallet {
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// The exchange rate of trading pair was set/updated with value from source. \[data_source], [trading_pair], [new value\]
ExchangeRateUpdated(MarketDataSourceString, TradingPairString, Option<ExchangeRate>),
ExchangeRateDeleted(MarketDataSourceString, TradingPairString),
AddedToWhitelist(MarketDataSourceString, [u8; 32]),
RemovedFromWhitelist(MarketDataSourceString, [u8; 32]),
ExchangeRateUpdated(DataSource, TradingPairString, Option<ExchangeRate>),
ExchangeRateDeleted(DataSource, TradingPairString),
OracleUpdated(OracleDataName, DataSource),
AddedToWhitelist(DataSource, [u8; 32]),
RemovedFromWhitelist(DataSource, [u8; 32]),
}

#[pallet::error]
Expand All @@ -106,7 +124,9 @@ pub mod pallet {
ReleaseNotWhitelisted,
ReleaseAlreadyWhitelisted,
TradingPairStringTooLong,
MarketDataSourceStringTooLong,
OracleDataNameStringTooLong,
DataSourceStringTooLong,
OracleBlobTooBig,
}

#[pallet::hooks]
Expand All @@ -117,11 +137,11 @@ pub mod pallet {
#[pallet::weight(<T as Config>::WeightInfo::add_to_whitelist())]
pub fn add_to_whitelist(
origin: OriginFor<T>,
data_source: MarketDataSourceString,
data_source: DataSource,
mrenclave: [u8; 32],
) -> DispatchResult {
ensure_root(origin)?;
ensure!(data_source.len() <= MAX_SOURCE_LEN, Error::<T>::MarketDataSourceStringTooLong);
ensure!(data_source.len() <= MAX_SOURCE_LEN, Error::<T>::DataSourceStringTooLong);
ensure!(
!Self::is_whitelisted(&data_source, mrenclave),
<Error<T>>::ReleaseAlreadyWhitelisted
Expand All @@ -136,7 +156,7 @@ pub mod pallet {
#[pallet::weight(<T as Config>::WeightInfo::remove_from_whitelist())]
pub fn remove_from_whitelist(
origin: OriginFor<T>,
data_source: MarketDataSourceString,
data_source: DataSource,
mrenclave: [u8; 32],
) -> DispatchResult {
ensure_root(origin)?;
Expand All @@ -151,10 +171,42 @@ pub mod pallet {
Ok(())
}

#[pallet::weight(<T as Config>::WeightInfo::update_oracle())]
pub fn update_oracle(
origin: OriginFor<T>,
oracle_name: OracleDataName,
data_source: DataSource,
new_blob: OracleDataBlob<T>,
) -> DispatchResultWithPostInfo {
let signer = ensure_signed(origin)?;
<pallet_teerex::Pallet<T>>::is_registered_enclave(&signer)?;
let signer_index = <pallet_teerex::Pallet<T>>::enclave_index(signer);
let signer_enclave = <pallet_teerex::Pallet<T>>::enclave(signer_index)
.ok_or(pallet_teerex::Error::<T>::EmptyEnclaveRegistry)?;

ensure!(
Self::is_whitelisted(&data_source, signer_enclave.mr_enclave),
<Error<T>>::ReleaseNotWhitelisted
);
ensure!(
oracle_name.len() <= MAX_ORACLE_DATA_NAME_LEN,
Error::<T>::OracleDataNameStringTooLong
);
ensure!(data_source.len() <= MAX_SOURCE_LEN, Error::<T>::DataSourceStringTooLong);
ensure!(
new_blob.len() as u32 <= T::MaxOracleBlobLen::get(),
Error::<T>::OracleBlobTooBig
);

OracleData::<T>::insert(&oracle_name, &data_source, new_blob);
Self::deposit_event(Event::<T>::OracleUpdated(oracle_name, data_source));
Ok(().into())
}

#[pallet::weight(<T as Config>::WeightInfo::update_exchange_rate())]
pub fn update_exchange_rate(
origin: OriginFor<T>,
data_source: MarketDataSourceString,
data_source: DataSource,
trading_pair: TradingPairString,
new_value: Option<ExchangeRate>,
) -> DispatchResultWithPostInfo {
Expand All @@ -163,6 +215,7 @@ pub mod pallet {
let sender_index = <pallet_teerex::Pallet<T>>::enclave_index(sender);
let sender_enclave = <pallet_teerex::Pallet<T>>::enclave(sender_index)
.ok_or(pallet_teerex::Error::<T>::EmptyEnclaveRegistry)?;
// Todo: Never checks data source len
ensure!(
trading_pair.len() <= MAX_TRADING_PAIR_LEN,
Error::<T>::TradingPairStringTooLong
Expand Down Expand Up @@ -191,7 +244,7 @@ pub mod pallet {
}
}
impl<T: Config> Pallet<T> {
fn is_whitelisted(data_source: &MarketDataSourceString, mrenclave: [u8; 32]) -> bool {
fn is_whitelisted(data_source: &DataSource, mrenclave: [u8; 32]) -> bool {
Self::whitelist(data_source).contains(&mrenclave)
}
}
Expand Down
16 changes: 9 additions & 7 deletions teeracle/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
limitations under the License.
*/
use crate as pallet_exchange;
use crate as pallet_teeracle;
use frame_support::{pallet_prelude::GenesisBuild, parameter_types};
use frame_system as system;
use pallet_exchange::Config;
use pallet_teeracle::Config;
use sp_core::H256;
use sp_keyring::AccountKeyring;
use sp_runtime::{
Expand Down Expand Up @@ -49,11 +49,11 @@ frame_support::construct_runtime!(
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
Timestamp: timestamp::{Pallet, Call, Storage, Inherent},
Teerex: pallet_teerex::{Pallet, Call, Storage, Event<T>},
Exchange: pallet_exchange::{Pallet, Call, Storage, Event<T>},
System: frame_system,
Balances: pallet_balances,
Timestamp: timestamp,
Teerex: pallet_teerex,
Teeracle: pallet_teeracle,
}
);

Expand Down Expand Up @@ -122,6 +122,7 @@ parameter_types! {
pub const MomentsPerDay: u64 = 86_400_000; // [ms/d]
pub const MaxSilenceTime: u64 = 172_800_000; // 48h
pub const MaxWhitelistedReleases: u32 = 10;
pub const MaxOracleBlobLen: u32 = 4096;
}

impl pallet_teerex::Config for Test {
Expand All @@ -136,6 +137,7 @@ impl Config for Test {
type Event = Event;
type WeightInfo = ();
type MaxWhitelistedReleases = MaxWhitelistedReleases;
type MaxOracleBlobLen = MaxOracleBlobLen;
}

// This function basically just builds a genesis storage key/value store according to
Expand Down
Loading

0 comments on commit ed263bc

Please sign in to comment.