From c46a5ee19042403c028b2f332e6b030bbc83af84 Mon Sep 17 00:00:00 2001
From: Xiliang Chen <xlchen1291@gmail.com>
Date: Thu, 21 May 2020 00:04:05 +1200
Subject: [PATCH] Oracle (#169)

* refactor oracle

* use SignedExtension

* fix tests

* finish oracle tests
---
 oracle/Cargo.toml               |   8 +-
 oracle/src/lib.rs               | 276 +++++++++++++++++---------
 oracle/src/mock.rs              |  62 +++---
 oracle/src/operator_provider.rs |  19 --
 oracle/src/tests.rs             | 342 ++++++++++++++++++++------------
 utilities/src/lib.rs            |   1 +
 utilities/src/ordered_set.rs    |   2 +-
 7 files changed, 440 insertions(+), 270 deletions(-)
 delete mode 100644 oracle/src/operator_provider.rs

diff --git a/oracle/Cargo.toml b/oracle/Cargo.toml
index efaf200..aff8bc4 100644
--- a/oracle/Cargo.toml
+++ b/oracle/Cargo.toml
@@ -10,8 +10,10 @@ edition = "2018"
 [dependencies]
 serde = { version = "1.0.101", optional = true }
 codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false }
-sp-runtime = { version = "2.0.0-alpha.8", default-features = false }
+
+sp-application-crypto = { version = "2.0.0-alpha.8", default-features = false }
 sp-io = { version = "2.0.0-alpha.8", default-features = false }
+sp-runtime = { version = "2.0.0-alpha.8", default-features = false }
 sp-std = { version = "2.0.0-alpha.8", default-features = false }
 
 frame-support = { version = "2.0.0-alpha.8", default-features = false }
@@ -22,7 +24,6 @@ orml-utilities = { path = "../utilities", version = "0.1.0", default-features =
 
 [dev-dependencies]
 sp-core = { version = "2.0.0-alpha.8", default-features = false }
-pallet-timestamp = { version = "2.0.0-alpha.8" }
 
 clear_on_drop = { version = "0.2.3", features = ["no_cc"] }	# https://github.com/paritytech/substrate/issues/4179
 
@@ -31,8 +32,9 @@ default = ["std"]
 std = [
 	"serde",
 	"codec/std",
-	"sp-runtime/std",
+	"sp-application-crypto/std",
 	"sp-io/std",
+	"sp-runtime/std",
 	"sp-std/std",
 	"frame-support/std",
 	"frame-system/std",
diff --git a/oracle/src/lib.rs b/oracle/src/lib.rs
index 8c2b7ac..9d19523 100644
--- a/oracle/src/lib.rs
+++ b/oracle/src/lib.rs
@@ -4,36 +4,54 @@
 
 mod default_combine_data;
 mod mock;
-mod operator_provider;
 mod tests;
 mod timestamped_value;
 
 use codec::{Decode, Encode};
-pub use default_combine_data::DefaultCombineData;
 use frame_support::{
 	decl_error, decl_event, decl_module, decl_storage,
 	dispatch::Dispatchable,
 	ensure,
-	traits::Time,
-	weights::{DispatchClass, FunctionOf, Pays, TransactionPriority},
+	traits::{ChangeMembers, Get, InitializeMembers, Time},
+	weights::{DispatchClass, FunctionOf, Pays},
 	IsSubType, IterableStorageMap, Parameter,
 };
-pub use operator_provider::OperatorProvider;
 use sp_runtime::{
 	traits::{DispatchInfoOf, Member, SignedExtension},
+	transaction_validity::{
+		InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransaction,
+	},
 	DispatchResult,
 };
-use sp_std::{fmt, marker, prelude::*, result, vec};
+use sp_std::{fmt, prelude::*, result, vec};
 // FIXME: `pallet/frame-` prefix should be used for all pallet modules, but currently `frame_system`
 // would cause compiling error in `decl_module!` and `construct_runtime!`
 // #3295 https://github.com/paritytech/substrate/issues/3295
-use frame_system::{self as system, ensure_signed};
+pub use default_combine_data::DefaultCombineData;
+use frame_system::{self as system, ensure_none, ensure_signed};
 pub use orml_traits::{CombineData, DataProvider, DataProviderExtended, OnNewData, OnRedundantCall};
-use sp_runtime::transaction_validity::{
-	InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,
-};
+use orml_utilities::OrderedSet;
 pub use timestamped_value::TimestampedValue;
 
+use sp_application_crypto::{KeyTypeId, RuntimeAppPublic};
+pub const ORACLE: KeyTypeId = KeyTypeId(*b"orac");
+
+mod app_sr25519 {
+	use sp_application_crypto::{app_crypto, sr25519};
+	app_crypto!(sr25519, super::ORACLE);
+}
+
+sp_application_crypto::with_pair! {
+	/// An oracle keypair using sr25519 as its crypto.
+	pub type AuthorityPair = app_sr25519::Pair;
+}
+
+/// An oracle signature using sr25519 as its crypto.
+pub type AuthoritySignature = app_sr25519::Signature;
+
+/// An oracle identifier using sr25519 as its crypto.
+pub type AuthorityId = app_sr25519::Public;
+
 type MomentOf<T> = <<T as Trait>::Time as Time>::Moment;
 pub type TimestampedValueOf<T> = TimestampedValue<<T as Trait>::OracleValue, MomentOf<T>>;
 
@@ -41,25 +59,48 @@ pub trait Trait: frame_system::Trait {
 	type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
 	type Call: Parameter + Dispatchable<Origin = <Self as frame_system::Trait>::Origin> + IsSubType<Module<Self>, Self>;
 	type OnNewData: OnNewData<Self::AccountId, Self::OracleKey, Self::OracleValue>;
-	type OnRedundantCall: OnRedundantCall<Self::AccountId>;
-	type OperatorProvider: OperatorProvider<Self::AccountId>;
 	type CombineData: CombineData<Self::OracleKey, TimestampedValueOf<Self>>;
 	type Time: Time;
 	type OracleKey: Parameter + Member;
 	type OracleValue: Parameter + Member + Ord;
+
+	/// A configuration for base priority of unsigned transactions.
+	///
+	/// This is exposed so that it can be tuned for particular runtime, when
+	/// multiple pallets send unsigned transactions.
+	type UnsignedPriority: Get<TransactionPriority>;
+
+	/// The identifier type for an authority.
+	type AuthorityId: Member + Parameter + RuntimeAppPublic + Default + Ord;
 }
 
 decl_storage! {
 	trait Store for Module<T: Trait> as Oracle {
-		pub RawValues get(fn raw_values): double_map hasher(twox_64_concat) T::OracleKey, hasher(twox_64_concat) T::AccountId => Option<TimestampedValueOf<T>>;
-		pub HasUpdate get(fn has_update): map hasher(twox_64_concat) T::OracleKey => bool;
+
+		/// Raw values for each oracle operators
+		pub RawValues get(fn raw_values): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::OracleKey => Option<TimestampedValueOf<T>>;
+
+		/// True if Self::values(key) is up to date, otherwise the value is stale
+		pub IsUpdated get(fn is_updated): map hasher(twox_64_concat) T::OracleKey => bool;
+
+		/// Combined value, may not be up to date
 		pub Values get(fn values): map hasher(twox_64_concat) T::OracleKey => Option<TimestampedValueOf<T>>;
-		HasDispatched: Vec<T::AccountId>;
+
+		/// If an oracle operator has feed a value in this block
+		HasDispatched: OrderedSet<T::AccountId>;
+
+		// TODO: this shouldn't be required https://github.com/paritytech/substrate/issues/6041
+		/// The current members of the collective. This is stored sorted (just by value).
+		pub Members get(fn members) config(): OrderedSet<T::AccountId>;
+
+		/// Session key for oracle operators
+		pub SessionKeys get(fn session_keys) config(): map hasher(twox_64_concat) T::AccountId => Option<T::AuthorityId>;
+
+		pub Nonces get(fn nonces): map hasher(twox_64_concat) T::AccountId => u32;
 	}
 }
 
 decl_error! {
-	// Oracle module errors
 	pub enum Error for Module<T: Trait> {
 		NoPermission,
 		UpdateAlreadyDispatched,
@@ -72,21 +113,31 @@ pub enum ValidityError {
 }
 
 decl_module! {
-	#[derive(Encode, Decode)]
 	pub struct Module<T: Trait> for enum Call where origin: T::Origin {
 		type Error = Error<T>;
+
 		fn deposit_event() = default;
 
 		#[weight = FunctionOf(0, DispatchClass::Operational, Pays::No)]
-		pub fn feed_value(origin, key: T::OracleKey, value: T::OracleValue) {
-			let who = ensure_signed(origin)?;
-			Self::_feed_values(who, vec![(key, value)])?;
+		pub fn feed_values(
+			origin,
+			values: Vec<(T::OracleKey, T::OracleValue)>,
+			#[compact] index: u32,
+			// since signature verification is done in `validate_unsigned`
+			// we can skip doing it here again.
+			_signature: <T::AuthorityId as RuntimeAppPublic>::Signature,
+		) {
+			ensure_none(origin)?;
+			let who = Self::members().0[index as usize].clone();
+			Self::_feed_values(who, values)?;
 		}
 
-		#[weight = FunctionOf(0, DispatchClass::Operational, Pays::No)]
-		pub fn feed_values(origin, values: Vec<(T::OracleKey, T::OracleValue)>) {
+		#[weight = 10_000_000]
+		pub fn set_session_key(origin, key: T::AuthorityId) {
 			let who = ensure_signed(origin)?;
-			Self::_feed_values(who, values)?;
+			ensure!(Self::members().contains(&who), Error::<T>::NoPermission);
+
+			SessionKeys::<T>::insert(who, key);
 		}
 
 		fn on_finalize(_n: T::BlockNumber) {
@@ -109,9 +160,10 @@ decl_event!(
 
 impl<T: Trait> Module<T> {
 	pub fn read_raw_values(key: &T::OracleKey) -> Vec<TimestampedValueOf<T>> {
-		T::OperatorProvider::operators()
+		Self::members()
+			.0
 			.iter()
-			.filter_map(|x| <RawValues<T>>::get(key, x))
+			.filter_map(|x| Self::raw_values(x, key))
 			.collect()
 	}
 
@@ -119,22 +171,24 @@ impl<T: Trait> Module<T> {
 	///
 	/// Note this will update values storage if has update.
 	pub fn get(key: &T::OracleKey) -> Option<TimestampedValueOf<T>> {
-		if <HasUpdate<T>>::take(key) {
+		if Self::is_updated(key) {
+			<Values<T>>::get(key)
+		} else {
 			let timestamped = Self::combined(key)?;
 			<Values<T>>::insert(key, timestamped.clone());
-			return Some(timestamped);
+			IsUpdated::<T>::insert(key, true);
+			Some(timestamped)
 		}
-		<Values<T>>::get(key)
 	}
 
 	/// Returns fresh combined value if has update, or latest combined value.
 	///
 	/// This is a no-op function which would not change storage.
 	pub fn get_no_op(key: &T::OracleKey) -> Option<TimestampedValueOf<T>> {
-		if Self::has_update(key) {
-			Self::combined(key)
-		} else {
+		if Self::is_updated(key) {
 			Self::values(key)
+		} else {
+			Self::combined(key)
 		}
 	}
 
@@ -149,14 +203,74 @@ impl<T: Trait> Module<T> {
 		let values = Self::read_raw_values(key);
 		T::CombineData::combine_data(key, values, Self::values(key))
 	}
+
+	fn _feed_values(who: T::AccountId, values: Vec<(T::OracleKey, T::OracleValue)>) -> DispatchResult {
+		let now = T::Time::now();
+
+		for (key, value) in &values {
+			let timestamped = TimestampedValue {
+				value: value.clone(),
+				timestamp: now,
+			};
+			RawValues::<T>::insert(&who, &key, timestamped);
+			IsUpdated::<T>::remove(&key);
+
+			T::OnNewData::on_new_data(&who, &key, &value);
+		}
+
+		Self::deposit_event(RawEvent::NewFeedData(who, values));
+
+		Ok(())
+	}
+}
+
+impl<T: Trait> InitializeMembers<T::AccountId> for Module<T> {
+	fn initialize_members(members: &[T::AccountId]) {
+		if !members.is_empty() {
+			assert!(Members::<T>::get().0.is_empty(), "Members are already initialized!");
+			Members::<T>::put(OrderedSet::from_sorted_set(members.into()));
+		}
+	}
+}
+
+impl<T: Trait> ChangeMembers<T::AccountId> for Module<T> {
+	fn change_members_sorted(_incoming: &[T::AccountId], outgoing: &[T::AccountId], new: &[T::AccountId]) {
+		// remove session keys and its values
+		for removed in outgoing {
+			SessionKeys::<T>::remove(removed);
+			RawValues::<T>::remove_prefix(removed);
+			Nonces::<T>::remove(removed);
+		}
+
+		Members::<T>::put(OrderedSet::from_sorted_set(new.into()));
+
+		// not bothering to track which key needs recompute, just update all
+		IsUpdated::<T>::remove_all();
+	}
+
+	fn set_prime(_prime: Option<T::AccountId>) {
+		// nothing
+	}
+}
+
+impl<T: Trait> DataProvider<T::OracleKey, T::OracleValue> for Module<T> {
+	fn get(key: &T::OracleKey) -> Option<T::OracleValue> {
+		Self::get(key).map(|timestamped_value| timestamped_value.value)
+	}
+}
+
+impl<T: Trait> DataProviderExtended<T::OracleKey, T::OracleValue, T::AccountId> for Module<T> {
+	fn feed_value(who: T::AccountId, key: T::OracleKey, value: T::OracleValue) -> DispatchResult {
+		Self::_feed_values(who, vec![(key, value)])
+	}
 }
 
 #[derive(Encode, Decode, Clone, Eq, PartialEq, Default)]
-pub struct CheckOperator<T: Trait + Send + Sync>(marker::PhantomData<T>);
+pub struct CheckOperator<T: Trait + Send + Sync>(sp_std::marker::PhantomData<T>);
 
 impl<T: Trait + Send + Sync> CheckOperator<T> {
 	pub fn new() -> Self {
-		Self(marker::PhantomData)
+		Self(sp_std::marker::PhantomData)
 	}
 }
 
@@ -183,73 +297,45 @@ impl<T: Trait + Send + Sync> SignedExtension for CheckOperator<T> {
 		Ok(())
 	}
 
-	fn validate(
-		&self,
-		who: &T::AccountId,
-		call: &Self::Call,
-		_info: &DispatchInfoOf<Self::Call>,
-		_len: usize,
-	) -> TransactionValidity {
+	fn validate_unsigned(call: &Self::Call, _info: &DispatchInfoOf<Self::Call>, _len: usize) -> TransactionValidity {
 		let call = match call.is_sub_type() {
 			Some(call) => call,
 			None => return Ok(ValidTransaction::default()),
 		};
 
-		if let Call::<T>::feed_value(..) | Call::<T>::feed_values(..) = call {
-			ensure!(
-				T::OperatorProvider::can_feed_data(who),
-				TransactionValidityError::Invalid(InvalidTransaction::Custom(ValidityError::NoPermission as u8))
-			);
-
-			return Ok(ValidTransaction {
-				priority: TransactionPriority::max_value(),
-				..Default::default()
-			});
-		}
-		Ok(ValidTransaction::default())
-	}
-}
-
-impl<T: Trait> DataProvider<T::OracleKey, T::OracleValue> for Module<T> {
-	fn get(key: &T::OracleKey) -> Option<T::OracleValue> {
-		Self::get(key).map(|timestamped_value| timestamped_value.value)
-	}
-}
-
-impl<T: Trait> DataProviderExtended<T::OracleKey, T::OracleValue, T::AccountId> for Module<T> {
-	fn feed_value(who: T::AccountId, key: T::OracleKey, value: T::OracleValue) -> DispatchResult {
-		Self::_feed_values(who, vec![(key, value)])
-	}
-}
-
-impl<T: Trait> Module<T> {
-	fn _feed_values(who: T::AccountId, values: Vec<(T::OracleKey, T::OracleValue)>) -> DispatchResult {
-		ensure!(T::OperatorProvider::can_feed_data(&who), Error::<T>::NoPermission);
-
-		// ensure account hasn't dispatched an updated yet
-		let mut accounts = <HasDispatched<T>>::get();
-		if accounts.contains(&who) {
-			T::OnRedundantCall::multiple_calls_per_block(&who);
-			return Err(Error::<T>::UpdateAlreadyDispatched.into());
-		}
-		accounts.push(who.clone());
-		<HasDispatched<T>>::put(accounts);
-
-		let now = T::Time::now();
-
-		for (key, value) in &values {
-			let timestamped = TimestampedValue {
-				value: value.clone(),
-				timestamp: now,
-			};
-			<RawValues<T>>::insert(&key, &who, timestamped);
-			<HasUpdate<T>>::insert(&key, true);
-
-			T::OnNewData::on_new_data(&who, &key, &value);
+		if let Call::feed_values(value, index, signature) = call {
+			let members = Module::<T>::members();
+			let who = members.0.get(*index as usize);
+			if let Some(who) = who {
+				let nonce = Module::<T>::nonces(&who);
+
+				let signature_valid = Module::<T>::session_keys(&who)
+					.map(|session_key| (nonce, value).using_encoded(|payload| session_key.verify(&payload, &signature)))
+					.unwrap_or(false);
+
+				if !signature_valid {
+					return InvalidTransaction::BadProof.into();
+				}
+
+				// ensure account hasn't dispatched an updated yet
+				let ok = HasDispatched::<T>::mutate(|set| set.insert(who.clone()));
+				if !ok {
+					// we already received a feed for this operator
+					return Err(InvalidTransaction::Stale.into());
+				}
+
+				Nonces::<T>::insert(who, nonce + 1);
+
+				ValidTransaction::with_tag_prefix("Oracle")
+					.priority(T::UnsignedPriority::get())
+					.longevity(256)
+					.propagate(true)
+					.build()
+			} else {
+				InvalidTransaction::BadProof.into()
+			}
+		} else {
+			InvalidTransaction::Call.into()
 		}
-
-		Self::deposit_event(RawEvent::NewFeedData(who, values));
-
-		Ok(())
 	}
 }
diff --git a/oracle/src/mock.rs b/oracle/src/mock.rs
index c48ebd6..38d6e56 100644
--- a/oracle/src/mock.rs
+++ b/oracle/src/mock.rs
@@ -5,10 +5,11 @@ use super::*;
 use frame_support::{impl_outer_dispatch, impl_outer_origin, parameter_types, weights::Weight};
 use sp_core::H256;
 use sp_runtime::{
-	testing::Header,
+	testing::{Header, UintAuthorityId},
 	traits::{BlakeTwo256, IdentityLookup},
 	Perbill,
 };
+use std::cell::RefCell;
 
 impl_outer_origin! {
 	pub enum Origin for Test {}
@@ -21,6 +22,9 @@ impl_outer_dispatch! {
 }
 
 pub type OracleCall = super::Call<Test>;
+type AccountId = u64;
+type Key = u32;
+type Value = u32;
 
 // For testing the module, we construct most of a mock runtime. This means
 // first constructing a configuration type (`Test`) which `impl`s each of the
@@ -40,7 +44,7 @@ impl frame_system::Trait for Test {
 	type BlockNumber = u64;
 	type Hash = H256;
 	type Hashing = BlakeTwo256;
-	type AccountId = u64;
+	type AccountId = AccountId;
 	type Lookup = IdentityLookup<Self::AccountId>;
 	type Header = Header;
 	type Event = ();
@@ -58,55 +62,59 @@ impl frame_system::Trait for Test {
 	type ExtrinsicBaseWeight = ();
 }
 
-type AccountId = u64;
-type Key = u32;
-type Value = u32;
-
-pub type Timestamp = pallet_timestamp::Module<Test>;
-
-parameter_types! {
-	pub const MinimumPeriod: u64 = 5;
-}
-
-impl pallet_timestamp::Trait for Test {
-	type Moment = u64;
-	type OnTimestampSet = ();
-	type MinimumPeriod = MinimumPeriod;
+thread_local! {
+	static TIME: RefCell<u32> = RefCell::new(0);
 }
 
-pub struct MockOperatorProvider;
+pub struct Timestamp;
+impl Time for Timestamp {
+	type Moment = u32;
 
-impl OperatorProvider<AccountId> for MockOperatorProvider {
-	fn can_feed_data(who: &AccountId) -> bool {
-		Self::operators().contains(who)
+	fn now() -> Self::Moment {
+		TIME.with(|v| *v.borrow())
 	}
+}
 
-	fn operators() -> Vec<AccountId> {
-		vec![1, 2, 3]
+impl Timestamp {
+	pub fn set_timestamp(val: u32) {
+		TIME.with(|v| *v.borrow_mut() = val);
 	}
 }
 
 parameter_types! {
 	pub const MinimumCount: u32 = 3;
 	pub const ExpiresIn: u32 = 600;
+	pub const UnsignedPriority: TransactionPriority = 32u64;
 }
 
 impl Trait for Test {
 	type Event = ();
 	type Call = Call;
 	type OnNewData = ();
-	type OnRedundantCall = ();
-	type OperatorProvider = MockOperatorProvider;
 	type CombineData = DefaultCombineData<Self, MinimumCount, ExpiresIn>;
-	type Time = pallet_timestamp::Module<Self>;
+	type Time = Timestamp;
 	type OracleKey = Key;
 	type OracleValue = Value;
+	type UnsignedPriority = UnsignedPriority;
+	type AuthorityId = UintAuthorityId;
 }
 pub type ModuleOracle = Module<Test>;
 // This function basically just builds a genesis storage key/value store according to
 // our desired mockup.
 pub fn new_test_ext() -> sp_io::TestExternalities {
-	let r = frame_system::GenesisConfig::default().build_storage::<Test>();
+	let mut storage = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
+
+	let _ = GenesisConfig::<Test> {
+		members: vec![1, 2, 3].into(),
+		session_keys: vec![(1, 10.into()), (2, 20.into()), (3, 30.into())],
+	}
+	.assimilate_storage(&mut storage);
+
+	let mut t: sp_io::TestExternalities = storage.into();
+
+	t.execute_with(|| {
+		Timestamp::set_timestamp(12345);
+	});
 
-	r.unwrap().into()
+	t
 }
diff --git a/oracle/src/operator_provider.rs b/oracle/src/operator_provider.rs
deleted file mode 100644
index 40cfad9..0000000
--- a/oracle/src/operator_provider.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use sp_std::prelude::Vec;
-
-pub trait OperatorProvider<AccountId> {
-	// Make sure `who` has permission to feed data
-	fn can_feed_data(who: &AccountId) -> bool;
-
-	// return a list of operators
-	fn operators() -> Vec<AccountId>;
-}
-
-impl<AccountId> OperatorProvider<AccountId> for () {
-	fn can_feed_data(_who: &AccountId) -> bool {
-		false
-	}
-
-	fn operators() -> Vec<AccountId> {
-		Vec::new()
-	}
-}
diff --git a/oracle/src/tests.rs b/oracle/src/tests.rs
index 6d3572d..5c76de1 100644
--- a/oracle/src/tests.rs
+++ b/oracle/src/tests.rs
@@ -2,33 +2,51 @@
 
 use crate::{
 	mock::{new_test_ext, Call, ModuleOracle, OracleCall, Origin, Test, Timestamp},
-	{CheckOperator, Error, TimestampedValue},
+	{CheckOperator, TimestampedValue},
 };
+use codec::Encode;
 use frame_support::{
-	assert_noop, assert_ok,
-	traits::OnFinalize,
-	weights::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays, TransactionPriority},
+	assert_noop, assert_ok, dispatch,
+	traits::{ChangeMembers, OnFinalize},
+	weights::{DispatchClass, DispatchInfo, Pays},
+};
+use sp_runtime::{
+	testing::UintAuthorityId,
+	traits::SignedExtension,
+	transaction_validity::{InvalidTransaction, TransactionValidityError},
+	RuntimeAppPublic,
 };
-use sp_runtime::{traits::SignedExtension, transaction_validity::ValidTransaction};
-
-#[test]
-fn should_feed_value() {
-	new_test_ext().execute_with(|| {
-		let key: u32 = 1;
-		let account_id: u64 = 1;
-
-		Timestamp::set_timestamp(12345);
 
-		let expected = TimestampedValue {
-			value: 1000,
-			timestamp: 12345,
-		};
+fn feed_values_from_session_key(
+	id: UintAuthorityId,
+	index: u32,
+	nonce: u32,
+	values: Vec<(u32, u32)>,
+) -> Result<dispatch::DispatchResult, TransactionValidityError> {
+	let sig = id.sign(&(nonce, &values).encode()).unwrap();
+
+	CheckOperator::<Test>::validate_unsigned(
+		&Call::ModuleOracle(OracleCall::feed_values(values.clone(), index, sig.clone())),
+		&DispatchInfo {
+			weight: 0,
+			class: DispatchClass::Normal,
+			pays_fee: Pays::Yes,
+		},
+		0,
+	)?;
+
+	Ok(ModuleOracle::feed_values(Origin::NONE, values, index, sig))
+}
 
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(account_id), key, 1000));
+fn feed_values(
+	from: u64,
+	index: u32,
+	nonce: u32,
+	values: Vec<(u32, u32)>,
+) -> Result<dispatch::DispatchResult, TransactionValidityError> {
+	let id = ModuleOracle::session_keys(from).unwrap();
 
-		let feed_data = ModuleOracle::raw_values(&key, &account_id).unwrap();
-		assert_eq!(feed_data, expected);
-	});
+	feed_values_from_session_key(id, index, nonce, values)
 }
 
 #[test]
@@ -36,15 +54,10 @@ fn should_feed_values() {
 	new_test_ext().execute_with(|| {
 		let account_id: u64 = 1;
 
-		Timestamp::set_timestamp(12345);
-
-		assert_ok!(ModuleOracle::feed_values(
-			Origin::signed(account_id),
-			vec![(1, 1000), (2, 900), (3, 800)]
-		));
+		assert_ok!(feed_values(account_id, 0, 0, vec![(50, 1000), (51, 900), (52, 800)]));
 
 		assert_eq!(
-			ModuleOracle::raw_values(&1, &account_id),
+			ModuleOracle::raw_values(&account_id, &50),
 			Some(TimestampedValue {
 				value: 1000,
 				timestamp: 12345,
@@ -52,7 +65,7 @@ fn should_feed_values() {
 		);
 
 		assert_eq!(
-			ModuleOracle::raw_values(&2, &account_id),
+			ModuleOracle::raw_values(&account_id, &51),
 			Some(TimestampedValue {
 				value: 900,
 				timestamp: 12345,
@@ -60,7 +73,7 @@ fn should_feed_values() {
 		);
 
 		assert_eq!(
-			ModuleOracle::raw_values(&3, &account_id),
+			ModuleOracle::raw_values(&account_id, &52),
 			Some(TimestampedValue {
 				value: 800,
 				timestamp: 12345,
@@ -70,157 +83,151 @@ fn should_feed_values() {
 }
 
 #[test]
-fn should_change_status_when_feeding() {
+fn should_update_is_updated() {
 	new_test_ext().execute_with(|| {
-		let key: u32 = 1;
-		assert_eq!(ModuleOracle::has_update(key), false);
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(1), key, 1000));
-		assert_eq!(ModuleOracle::has_update(key), true);
+		let key: u32 = 50;
+		assert_eq!(ModuleOracle::is_updated(key), false);
+		assert_ok!(feed_values(1, 0, 0, vec![(key, 1000)]));
+		assert_ok!(feed_values(2, 1, 0, vec![(key, 1000)]));
+		assert_ok!(feed_values(3, 2, 0, vec![(key, 1000)]));
+		assert_eq!(ModuleOracle::is_updated(key), false);
+		assert_eq!(
+			ModuleOracle::get(&key).unwrap(),
+			TimestampedValue {
+				value: 1000,
+				timestamp: 12345
+			}
+		);
+		assert_eq!(ModuleOracle::is_updated(key), true);
+		ModuleOracle::on_finalize(1);
+		assert_ok!(feed_values(1, 0, 1, vec![(key, 1000)]));
+		assert_eq!(ModuleOracle::is_updated(key), false);
 	});
 }
 
 #[test]
-fn should_read_raw_values() {
+fn should_validate_index() {
 	new_test_ext().execute_with(|| {
-		let key: u32 = 1;
+		assert_noop!(
+			feed_values(1, 1, 0, vec![(50, 1000)]),
+			TransactionValidityError::Invalid(InvalidTransaction::BadProof)
+		);
 
-		let raw_values = ModuleOracle::read_raw_values(&key);
-		assert_eq!(raw_values, vec![]);
+		assert_noop!(
+			feed_values(2, 0, 0, vec![(50, 1000)]),
+			TransactionValidityError::Invalid(InvalidTransaction::BadProof)
+		);
+	});
+}
 
-		Timestamp::set_timestamp(12345);
+#[test]
+fn should_validate_nonce() {
+	new_test_ext().execute_with(|| {
+		assert_noop!(
+			feed_values(1, 0, 1, vec![(50, 1000)]),
+			TransactionValidityError::Invalid(InvalidTransaction::BadProof)
+		);
 
-		let expected = vec![
-			TimestampedValue {
-				value: 1000,
-				timestamp: 12345,
-			},
-			TimestampedValue {
-				value: 1200,
-				timestamp: 12345,
-			},
-		];
+		assert_ok!(feed_values(1, 0, 0, vec![(50, 1000)]));
 
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(1), key, 1000));
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(2), key, 1200));
+		assert_eq!(ModuleOracle::nonces(&1), 1);
+		ModuleOracle::on_finalize(1);
 
-		let raw_values = ModuleOracle::read_raw_values(&key);
-		assert_eq!(raw_values, expected);
+		assert_noop!(
+			feed_values(1, 0, 0, vec![(50, 1000)]),
+			TransactionValidityError::Invalid(InvalidTransaction::BadProof)
+		);
+
+		assert_ok!(feed_values(1, 0, 1, vec![(50, 1000)]));
+
+		assert_eq!(ModuleOracle::nonces(&1), 2);
 	});
 }
 
 #[test]
-fn should_combined_data() {
+fn should_read_raw_values() {
 	new_test_ext().execute_with(|| {
-		Timestamp::set_timestamp(12345);
+		let key: u32 = 50;
 
-		let expected = Some(TimestampedValue {
-			value: 1200,
-			timestamp: 12345,
-		});
+		let raw_values = ModuleOracle::read_raw_values(&key);
+		assert_eq!(raw_values, vec![]);
 
-		let key: u32 = 1;
+		assert_ok!(feed_values(1, 0, 0, vec![(key, 1000)]));
+		assert_ok!(feed_values(2, 1, 0, vec![(key, 1200)]));
 
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(1), key, 1300));
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(2), key, 1000));
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(3), key, 1200));
-		assert_eq!(ModuleOracle::get(&key), expected);
+		let raw_values = ModuleOracle::read_raw_values(&key);
+		assert_eq!(
+			raw_values,
+			vec![
+				TimestampedValue {
+					value: 1000,
+					timestamp: 12345,
+				},
+				TimestampedValue {
+					value: 1200,
+					timestamp: 12345,
+				},
+			]
+		);
 	});
 }
 
 #[test]
-fn should_return_prev_value() {
+fn should_combined_data() {
 	new_test_ext().execute_with(|| {
-		Timestamp::set_timestamp(12345);
+		let key: u32 = 50;
+
+		assert_ok!(feed_values(1, 0, 0, vec![(key, 1300)]));
+		assert_ok!(feed_values(2, 1, 0, vec![(key, 1000)]));
+		assert_ok!(feed_values(3, 2, 0, vec![(key, 1200)]));
 
 		let expected = Some(TimestampedValue {
 			value: 1200,
 			timestamp: 12345,
 		});
 
-		let key: u32 = 1;
-
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(1), key, 1300));
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(2), key, 1000));
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(3), key, 1200));
 		assert_eq!(ModuleOracle::get(&key), expected);
 
 		Timestamp::set_timestamp(23456);
 
-		// should return prev_value
 		assert_eq!(ModuleOracle::get(&key), expected);
 	});
 }
 
 #[test]
-fn should_return_none() {
-	new_test_ext().execute_with(|| {
-		let key: u32 = 1;
-		assert_eq!(ModuleOracle::get(&key), None);
-	});
-}
-
-#[test]
-fn should_validate() {
-	new_test_ext().execute_with(|| {
-		let call = Call::ModuleOracle(OracleCall::feed_values(vec![(1, 1)]));
-		let info = <Call as GetDispatchInfo>::get_dispatch_info(&call);
-
-		assert_eq!(
-			CheckOperator::<Test>(Default::default()).validate(&1, &call, &info, 1),
-			Ok(ValidTransaction {
-				priority: TransactionPriority::max_value(),
-				..Default::default()
-			})
-		);
-	});
-}
-
-#[test]
-fn should_be_free_operational() {
+fn should_return_none_for_non_exist_key() {
 	new_test_ext().execute_with(|| {
-		let feed_value = Call::ModuleOracle(OracleCall::feed_value(1, 1));
-		let feed_values = Call::ModuleOracle(OracleCall::feed_values(vec![(1, 1)]));
-		vec![feed_value, feed_values].iter().for_each(|f| {
-			let dispatch_info = <Call as GetDispatchInfo>::get_dispatch_info(&f);
-			assert_eq!(
-				dispatch_info,
-				DispatchInfo {
-					weight: 0,
-					class: DispatchClass::Operational,
-					pays_fee: Pays::No,
-				}
-			);
-		});
+		assert_eq!(ModuleOracle::get(&50), None);
 	});
 }
 
 #[test]
 fn multiple_calls_should_fail() {
 	new_test_ext().execute_with(|| {
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(1), 1, 1000));
+		assert_ok!(feed_values(1, 0, 0, vec![(50, 1300)]));
 		assert_noop!(
-			ModuleOracle::feed_value(Origin::signed(1), 1, 1200),
-			Error::<Test>::UpdateAlreadyDispatched
+			feed_values(1, 0, 1, vec![(50, 1300)]),
+			TransactionValidityError::Invalid(InvalidTransaction::Stale)
 		);
-		<ModuleOracle as OnFinalize<u64>>::on_finalize(1);
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(1), 1, 1200));
+
+		ModuleOracle::on_finalize(1);
+
+		assert_ok!(feed_values(1, 0, 1, vec![(50, 1300)]));
 	});
 }
 
 #[test]
 fn get_all_values_should_work() {
 	new_test_ext().execute_with(|| {
-		Timestamp::set_timestamp(12345);
-
 		let eur: u32 = 1;
 		let jpy: u32 = 2;
 
 		assert_eq!(ModuleOracle::get_all_values(), vec![]);
 
 		// feed eur & jpy
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(1), eur, 1300));
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(2), eur, 1000));
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(3), jpy, 9000));
+		assert_ok!(feed_values(1, 0, 0, vec![(eur, 1300)]));
+		assert_ok!(feed_values(2, 1, 0, vec![(eur, 1000)]));
+		assert_ok!(feed_values(3, 2, 0, vec![(jpy, 9000)]));
 
 		// not enough eur & jpy prices
 		assert_eq!(ModuleOracle::get(&eur), None);
@@ -228,11 +235,11 @@ fn get_all_values_should_work() {
 		assert_eq!(ModuleOracle::get_all_values(), vec![]);
 
 		// finalize block
-		<ModuleOracle as OnFinalize<u64>>::on_finalize(1);
+		ModuleOracle::on_finalize(1);
 
 		// feed eur & jpy
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(3), eur, 1200));
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(1), jpy, 8000));
+		assert_ok!(feed_values(3, 2, 1, vec![(eur, 1200)]));
+		assert_ok!(feed_values(1, 0, 1, vec![(jpy, 8000)]));
 
 		// enough eur prices
 		let eur_price = Some(TimestampedValue {
@@ -247,7 +254,7 @@ fn get_all_values_should_work() {
 		assert_eq!(ModuleOracle::get_all_values(), vec![(eur, eur_price)]);
 
 		// feed jpy
-		assert_ok!(ModuleOracle::feed_value(Origin::signed(2), jpy, 7000));
+		assert_ok!(feed_values(2, 1, 1, vec![(jpy, 7000)]));
 
 		// enough jpy prices
 		let jpy_price = Some(TimestampedValue {
@@ -259,3 +266,88 @@ fn get_all_values_should_work() {
 		assert_eq!(ModuleOracle::get_all_values(), vec![(eur, eur_price), (jpy, jpy_price)]);
 	});
 }
+
+#[test]
+fn bad_index() {
+	new_test_ext().execute_with(|| {
+		assert_noop!(
+			feed_values(1, 255, 0, vec![(50, 1000)]),
+			TransactionValidityError::Invalid(InvalidTransaction::BadProof)
+		);
+	});
+}
+
+#[test]
+fn change_member_should_work() {
+	new_test_ext().execute_with(|| {
+		<ModuleOracle as ChangeMembers<u64>>::change_members_sorted(&[4], &[1], &[2, 3, 4]);
+
+		assert_noop!(
+			feed_values_from_session_key(10.into(), 0, 0, vec![(50, 1000)]),
+			TransactionValidityError::Invalid(InvalidTransaction::BadProof)
+		);
+
+		assert_ok!(feed_values(2, 0, 0, vec![(50, 1000)]));
+
+		assert_noop!(
+			feed_values_from_session_key(40.into(), 2, 0, vec![(50, 1000)]),
+			TransactionValidityError::Invalid(InvalidTransaction::BadProof)
+		);
+
+		assert_eq!(ModuleOracle::session_keys(&4), None);
+
+		assert_ok!(ModuleOracle::set_session_key(Origin::signed(4), 40.into()));
+
+		assert_ok!(feed_values(4, 2, 0, vec![(50, 1000)]));
+	});
+}
+
+#[test]
+fn should_clear_is_updated_on_change_member() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(feed_values(1, 0, 0, vec![(50, 1000)]));
+		assert_ok!(feed_values(2, 1, 0, vec![(50, 1000)]));
+		assert_ok!(feed_values(3, 2, 0, vec![(50, 1000)]));
+
+		assert_eq!(
+			ModuleOracle::get(&50).unwrap(),
+			TimestampedValue {
+				value: 1000,
+				timestamp: 12345
+			}
+		);
+		assert_eq!(ModuleOracle::is_updated(50), true);
+
+		ModuleOracle::change_members_sorted(&[4], &[1], &[2, 3, 4]);
+
+		assert_eq!(ModuleOracle::is_updated(50), false);
+	});
+}
+
+#[test]
+fn should_clear_data_for_removed_members() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(feed_values(1, 0, 0, vec![(50, 1000)]));
+		assert_ok!(feed_values(2, 1, 0, vec![(50, 1000)]));
+
+		ModuleOracle::change_members_sorted(&[4], &[1], &[2, 3, 4]);
+
+		assert_eq!(ModuleOracle::raw_values(&1, 50), None);
+		assert_eq!(ModuleOracle::session_keys(&1), None);
+		assert_eq!(ModuleOracle::nonces(&1), 0);
+	});
+}
+
+#[test]
+fn change_session_key() {
+	new_test_ext().execute_with(|| {
+		assert_ok!(ModuleOracle::set_session_key(Origin::signed(1), 11.into()));
+
+		assert_noop!(
+			feed_values_from_session_key(10.into(), 0, 0, vec![(50, 1000)]),
+			TransactionValidityError::Invalid(InvalidTransaction::BadProof)
+		);
+
+		assert_ok!(feed_values_from_session_key(11.into(), 0, 0, vec![(50, 1000)]));
+	});
+}
diff --git a/utilities/src/lib.rs b/utilities/src/lib.rs
index 910f988..7fb5b28 100644
--- a/utilities/src/lib.rs
+++ b/utilities/src/lib.rs
@@ -6,3 +6,4 @@ pub mod ordered_set;
 
 pub use fixed_u128::FixedU128;
 pub use linked_item::{LinkedItem, LinkedList};
+pub use ordered_set::OrderedSet;
diff --git a/utilities/src/ordered_set.rs b/utilities/src/ordered_set.rs
index 56ec866..d8ea421 100644
--- a/utilities/src/ordered_set.rs
+++ b/utilities/src/ordered_set.rs
@@ -3,7 +3,7 @@ use sp_runtime::RuntimeDebug;
 use sp_std::prelude::*;
 
 #[derive(RuntimeDebug, PartialEq, Eq, Encode, Decode, Default)]
-pub struct OrderedSet<T>(Vec<T>);
+pub struct OrderedSet<T>(pub Vec<T>);
 
 impl<T: Ord> OrderedSet<T> {
 	pub fn new() -> Self {
-- 
GitLab