git.delta.rocks / unique-network / refs/commits / 1e14fe80093b

difftreelog

feat(collator-selection) add+remove invulnerable methods + tests + miscellaneous changes before more refactoring

Fahrrader2022-12-21parent: #9b89504.patch.diff
in: master

11 files changed

modifiednode/cli/src/chain_spec.rsdiffbeforeafterboth
--- a/node/cli/src/chain_spec.rs
+++ b/node/cli/src/chain_spec.rs
@@ -24,7 +24,7 @@
 use serde_json::map::Map;
 
 use up_common::types::opaque::*;
-use up_common::constants::GENESIS_CANDIDACY_BOND;
+use up_common::constants::{GENESIS_CANDIDACY_BOND, SESSION_LENGTH};
 
 #[cfg(feature = "unique-runtime")]
 pub use unique_runtime as default_runtime;
@@ -197,6 +197,7 @@
 					.map(|(acc, _)| acc)
 					.collect(),
 				candidacy_bond: GENESIS_CANDIDACY_BOND,
+				kick_threshold: SESSION_LENGTH,
 				..Default::default()
 			},
 			session: SessionConfig {
modifiedpallets/collator-selection/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/benchmarking.rs
+++ b/pallets/collator-selection/src/benchmarking.rs
@@ -112,8 +112,13 @@
 }
 
 fn register_candidates<T: Config>(count: u32) {
-	let candidates = (0..count).map(|c| account("candidate", c, SEED)).collect::<Vec<_>>();
-	assert!(<CandidacyBond<T>>::get() > 0u32.into(), "Bond cannot be zero!");
+	let candidates = (0..count)
+		.map(|c| account("candidate", c, SEED))
+		.collect::<Vec<_>>();
+	assert!(
+		<CandidacyBond<T>>::get() > 0u32.into(),
+		"Bond cannot be zero!"
+	);
 
 	for who in candidates {
 		T::Currency::make_free_balance_be(&who, <CandidacyBond<T>>::get() * 2u32.into());
@@ -200,7 +205,8 @@
 		whitelist!(leaving);
 	}: _(RawOrigin::Signed(leaving.clone()))
 	verify {
-		assert_last_event::<T>(Event::CandidateRemoved{account_id: leaving}.into());
+		// todo:collator verify these
+		assert_last_event::<T>(Event::CandidateRemoved{account_id: leaving, deposit_returned: bond / 2u32.into() }.into());
 	}
 
 	// worse case is paying a non-existing candidate account.
@@ -272,4 +278,8 @@
 	}
 }
 
-impl_benchmark_test_suite!(CollatorSelection, crate::mock::new_test_ext(), crate::mock::Test,);
+impl_benchmark_test_suite!(
+	CollatorSelection,
+	crate::mock::new_test_ext(),
+	crate::mock::Test,
+);
modifiedpallets/collator-selection/src/lib.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/lib.rs
+++ b/pallets/collator-selection/src/lib.rs
@@ -30,6 +30,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// todo:collator documentation
 //! Collator Selection pallet.
 //!
 //! A pallet to manage collators in a parachain.
@@ -109,7 +110,10 @@
 	};
 	use frame_system::{pallet_prelude::*, Config as SystemConfig};
 	use pallet_session::SessionManager;
-	use sp_runtime::traits::Convert;
+	use sp_runtime::{
+		Perbill,
+		traits::{One, Convert},
+	};
 	use sp_staking::SessionIndex;
 
 	type BalanceOf<T> =
@@ -136,6 +140,9 @@
 		/// Origin that can dictate updating parameters of this pallet.
 		type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;
 
+		/// Account Identifier that holds the chain's treasury.
+		type TreasuryAccountId: Get<Self::AccountId>;
+
 		/// Account Identifier from which the internal Pot is generated.
 		type PotId: Get<PalletId>;
 
@@ -152,8 +159,8 @@
 		/// Maximum number of invulnerables. This is enforced in code.
 		type MaxInvulnerables: Get<u32>;
 
-		// Will be kicked if block is not produced in threshold.
-		type KickThreshold: Get<Self::BlockNumber>;
+		/// If kicked, how much of the collator's deposit will be slashed and sent to the slash destination.
+		type SlashRatio: Get<Perbill>;
 
 		/// A stable ID for a validator.
 		type ValidatorId: Member + Parameter;
@@ -200,6 +207,13 @@
 		ValueQuery,
 	>;
 
+	/// Collator will be kicked if it does not produce a block within the threshold (does not apply to invulnerables).
+	///
+	/// Should be a multiple of session or things will get inconsistent. todo:collator reword?
+	#[pallet::storage]
+	#[pallet::getter(fn kick_threshold)]
+	pub type KickThreshold<T: Config> = StorageValue<_, T::BlockNumber, ValueQuery>;
+
 	/// Last block authored by collator.
 	#[pallet::storage]
 	#[pallet::getter(fn last_authored_block)]
@@ -224,6 +238,7 @@
 	pub struct GenesisConfig<T: Config> {
 		pub invulnerables: Vec<T::AccountId>,
 		pub candidacy_bond: BalanceOf<T>,
+		pub kick_threshold: T::BlockNumber,
 		pub desired_candidates: u32,
 	}
 
@@ -233,6 +248,7 @@
 			Self {
 				invulnerables: Default::default(),
 				candidacy_bond: Default::default(),
+				kick_threshold: T::BlockNumber::one(),
 				desired_candidates: Default::default(),
 			}
 		}
@@ -241,8 +257,10 @@
 	#[pallet::genesis_build]
 	impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
 		fn build(&self) {
-			let duplicate_invulnerables =
-				self.invulnerables.iter().collect::<std::collections::BTreeSet<_>>();
+			let duplicate_invulnerables = self
+				.invulnerables
+				.iter()
+				.collect::<std::collections::BTreeSet<_>>();
 			assert!(
 				duplicate_invulnerables.len() == self.invulnerables.len(),
 				"duplicate invulnerables in genesis."
@@ -258,6 +276,7 @@
 
 			<DesiredCandidates<T>>::put(&self.desired_candidates);
 			<CandidacyBond<T>>::put(&self.candidacy_bond);
+			<KickThreshold<T>>::put(&self.kick_threshold);
 			<Invulnerables<T>>::put(bounded_invulnerables);
 		}
 	}
@@ -265,11 +284,29 @@
 	#[pallet::event]
 	#[pallet::generate_deposit(pub(super) fn deposit_event)]
 	pub enum Event<T: Config> {
-		NewInvulnerables { invulnerables: Vec<T::AccountId> },
-		NewDesiredCandidates { desired_candidates: u32 },
-		NewCandidacyBond { bond_amount: BalanceOf<T> },
-		CandidateAdded { account_id: T::AccountId, deposit: BalanceOf<T> },
-		CandidateRemoved { account_id: T::AccountId },
+		NewDesiredCandidates {
+			desired_candidates: u32,
+		},
+		NewCandidacyBond {
+			bond_amount: BalanceOf<T>,
+		},
+		NewKickThreshold {
+			length_in_blocks: T::BlockNumber,
+		},
+		InvulnerableAdded {
+			invulnerable: T::AccountId,
+		},
+		InvulnerableRemoved {
+			invulnerable: T::AccountId,
+		},
+		CandidateAdded {
+			account_id: T::AccountId,
+			deposit: BalanceOf<T>,
+		},
+		CandidateRemoved {
+			account_id: T::AccountId,
+			deposit_returned: BalanceOf<T>,
+		},
 	}
 
 	// Errors inform users that something went wrong.
@@ -289,8 +326,12 @@
 		NotCandidate,
 		/// Too many invulnerables
 		TooManyInvulnerables,
+		/// Too few invulnerables
+		TooFewInvulnerables,
 		/// User is already an Invulnerable
 		AlreadyInvulnerable,
+		/// User is not an Invulnerable
+		NotInvulnerable,
 		/// Account has no associated validator ID
 		NoAssociatedValidatorId,
 		/// Validator ID is not yet registered
@@ -302,33 +343,61 @@
 
 	#[pallet::call]
 	impl<T: Config> Pallet<T> {
-		/// Set the list of invulnerable (fixed) collators.
-		#[pallet::weight(T::WeightInfo::set_invulnerables(new.len() as u32))]
-		pub fn set_invulnerables(
+		/// Add a collator to the list of invulnerable (fixed) collators.
+		#[pallet::weight(T::WeightInfo::set_invulnerables(1 as u32))] // todo:collator weight
+		pub fn add_invulnerable(
 			origin: OriginFor<T>,
-			new: Vec<T::AccountId>,
+			new: T::AccountId,
 		) -> DispatchResultWithPostInfo {
 			T::UpdateOrigin::ensure_origin(origin)?;
-			let bounded_invulnerables = BoundedVec::<_, T::MaxInvulnerables>::try_from(new)
-				.map_err(|_| Error::<T>::TooManyInvulnerables)?;
 
-			// check if the invulnerables have associated validator keys before they are set
-			for account_id in bounded_invulnerables.iter() {
-				let validator_key = T::ValidatorIdOf::convert(account_id.clone())
-					.ok_or(Error::<T>::NoAssociatedValidatorId)?;
-				ensure!(
-					T::ValidatorRegistration::is_registered(&validator_key),
-					Error::<T>::ValidatorNotRegistered
-				);
+			// check if the new invulnerable has associated validator keys before it is added
+			let validator_key = T::ValidatorIdOf::convert(new.clone())
+				.ok_or(Error::<T>::NoAssociatedValidatorId)?;
+			ensure!(
+				T::ValidatorRegistration::is_registered(&validator_key),
+				Error::<T>::ValidatorNotRegistered
+			);
+			// ensure!(!Self::invulnerables().contains(&new), Error::<T>::AlreadyInvulnerable);
+			if Self::invulnerables().contains(&new) {
+				return Ok(().into());
 			}
 
-			<Invulnerables<T>>::put(&bounded_invulnerables);
-			Self::deposit_event(Event::NewInvulnerables {
-				invulnerables: bounded_invulnerables.to_vec(),
-			});
+			<Invulnerables<T>>::try_append(new.clone())
+				.map_err(|_| Error::<T>::TooManyInvulnerables)?;
+			Self::deposit_event(Event::InvulnerableAdded { invulnerable: new });
 			Ok(().into())
 		}
 
+		/// Remove a collator from the list of invulnerable (fixed) collators.
+		#[pallet::weight(T::WeightInfo::set_invulnerables(1))] // todo:collator weight
+		pub fn remove_invulnerable(
+			origin: OriginFor<T>,
+			who: T::AccountId,
+		) -> DispatchResultWithPostInfo {
+			T::UpdateOrigin::ensure_origin(origin)?;
+
+			// let index = Self::invulnerables().into_iter().position(|r| r == who).ok_or(Error::<T>::NotInvulnerable)?;
+			<Invulnerables<T>>::try_mutate(|invulnerables| -> DispatchResult {
+				if invulnerables.len() <= 1 {
+					return Err(Error::<T>::TooFewInvulnerables.into());
+				}
+
+				let index = invulnerables
+					.into_iter()
+					.position(|r| *r == who)
+					.ok_or(Error::<T>::NotInvulnerable)?;
+				invulnerables.remove(index);
+				Ok(())
+			})?;
+			/*let bounded_invulnerables = BoundedVec::<_, T::MaxInvulnerables>::try_from(new)
+				.map_err(|_| Error::<T>::TooManyInvulnerables)?;
+
+			<Invulnerables<T>>::put(&bounded_invulnerables);*/
+			Self::deposit_event(Event::InvulnerableRemoved { invulnerable: who });
+			Ok(().into())
+		}
+
 		/// Set the ideal number of collators (not including the invulnerables).
 		/// If lowering this number, then the number of running collators could be higher than this figure.
 		/// Aside from that edge case, there should be no other way to have more collators than the desired number.
@@ -343,7 +412,9 @@
 				log::warn!("max > T::MaxCandidates; you might need to run benchmarks again");
 			}
 			<DesiredCandidates<T>>::put(&max);
-			Self::deposit_event(Event::NewDesiredCandidates { desired_candidates: max });
+			Self::deposit_event(Event::NewDesiredCandidates {
+				desired_candidates: max,
+			});
 			Ok(().into())
 		}
 
@@ -359,6 +430,22 @@
 			Ok(().into())
 		}
 
+		/// Set the length of the kick threshold.
+		/// Note that if the length is not a multiple of the session period, it might get inconsistent.
+		#[pallet::weight(T::WeightInfo::set_candidacy_bond())] // todo:collator weight
+		pub fn set_kick_threshold(
+			origin: OriginFor<T>,
+			kick_threshold: T::BlockNumber,
+		) -> DispatchResultWithPostInfo {
+			T::UpdateOrigin::ensure_origin(origin)?;
+			// todo:collator insert something to guarantee consistency?
+			<KickThreshold<T>>::put(kick_threshold);
+			Self::deposit_event(Event::NewKickThreshold {
+				length_in_blocks: kick_threshold,
+			});
+			Ok(().into())
+		}
+
 		/// Register this account as a collator candidate. The account must (a) already have
 		/// registered session keys and (b) be able to reserve the `CandidacyBond`.
 		///
@@ -369,8 +456,15 @@
 
 			// ensure we are below limit.
 			let length = <Candidates<T>>::decode_len().unwrap_or_default();
-			ensure!((length as u32) < Self::desired_candidates(), Error::<T>::TooManyCandidates);
-			ensure!(!Self::invulnerables().contains(&who), Error::<T>::AlreadyInvulnerable);
+			ensure!(
+				(length as u32) < Self::desired_candidates(),
+				Error::<T>::TooManyCandidates
+			);
+			// todo:collator really need it?
+			ensure!(
+				!Self::invulnerables().contains(&who),
+				Error::<T>::AlreadyInvulnerable
+			);
 
 			let validator_key = T::ValidatorIdOf::convert(who.clone())
 				.ok_or(Error::<T>::NoAssociatedValidatorId)?;
@@ -381,7 +475,10 @@
 
 			let deposit = Self::candidacy_bond();
 			// First authored block is current block plus kick threshold to handle session delay
-			let incoming = CandidateInfo { who: who.clone(), deposit };
+			let incoming = CandidateInfo {
+				who: who.clone(),
+				deposit,
+			};
 
 			let current_count =
 				<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {
@@ -389,16 +486,21 @@
 						Err(Error::<T>::AlreadyCandidate)?
 					} else {
 						T::Currency::reserve(&who, deposit)?;
-						candidates.try_push(incoming).map_err(|_| Error::<T>::TooManyCandidates)?;
+						candidates
+							.try_push(incoming)
+							.map_err(|_| Error::<T>::TooManyCandidates)?;
 						<LastAuthoredBlock<T>>::insert(
 							who.clone(),
-							frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(),
+							frame_system::Pallet::<T>::block_number() + Self::kick_threshold(),
 						);
 						Ok(candidates.len())
 					}
 				})?;
 
-			Self::deposit_event(Event::CandidateAdded { account_id: who, deposit });
+			Self::deposit_event(Event::CandidateAdded {
+				account_id: who,
+				deposit,
+			});
 			Ok(Some(T::WeightInfo::register_as_candidate(current_count as u32)).into())
 		}
 
@@ -411,11 +513,12 @@
 		#[pallet::weight(T::WeightInfo::leave_intent(T::MaxCandidates::get()))]
 		pub fn leave_intent(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
 			let who = ensure_signed(origin)?;
+			// todo:collator invulnerables and candidates should count against min candidates together
 			ensure!(
 				Self::candidates().len() as u32 > T::MinCandidates::get(),
 				Error::<T>::TooFewCandidates
 			);
-			let current_count = Self::try_remove_candidate(&who)?;
+			let current_count = Self::try_remove_candidate(&who, false)?;
 
 			Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into())
 		}
@@ -427,8 +530,12 @@
 			T::PotId::get().into_account_truncating()
 		}
 
-		/// Removes a candidate if they exist and sends them back their deposit
-		fn try_remove_candidate(who: &T::AccountId) -> Result<usize, DispatchError> {
+		/// Removes a candidate if they exist and sends them back their deposit, optionally slashed.
+		fn try_remove_candidate(
+			who: &T::AccountId,
+			should_slash: bool,
+		) -> Result<usize, DispatchError> {
+			let mut deposit_returned = BalanceOf::<T>::default();
 			let current_count =
 				<Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {
 					let index = candidates
@@ -436,11 +543,33 @@
 						.position(|candidate| candidate.who == *who)
 						.ok_or(Error::<T>::NotCandidate)?;
 					let candidate = candidates.remove(index);
-					T::Currency::unreserve(who, candidate.deposit);
+					let deposit = candidate.deposit;
+
+					if should_slash {
+						let slashed = T::SlashRatio::get() * deposit;
+						let remaining = deposit - slashed;
+
+						let (imbalance, _) = T::Currency::slash_reserved(who, slashed);
+						//T::Currency::unreserve(who, remaining);
+						deposit_returned = remaining;
+
+						T::Currency::resolve_creating(&T::TreasuryAccountId::get(), imbalance);
+
+						// Self::deposit_event(Event::CandidateSlashed(who.clone()));
+					} else {
+						//T::Currency::unreserve(who, deposit);
+						deposit_returned = deposit;
+					}
+
+					T::Currency::unreserve(who, deposit_returned);
+					// candidates.remove(index);
 					<LastAuthoredBlock<T>>::remove(who.clone());
 					Ok(candidates.len())
 				})?;
-			Self::deposit_event(Event::CandidateRemoved { account_id: who.clone() });
+			Self::deposit_event(Event::CandidateRemoved {
+				account_id: who.clone(),
+				deposit_returned,
+			});
 			Ok(current_count)
 		}
 
@@ -456,12 +585,12 @@
 		}
 
 		/// Kicks out candidates that did not produce a block in the kick threshold
-		/// and refund their deposits.
+		/// and **confiscates** their deposits to the treasury.
 		pub fn kick_stale_candidates(
 			candidates: BoundedVec<CandidateInfo<T::AccountId, BalanceOf<T>>, T::MaxCandidates>,
 		) -> BoundedVec<T::AccountId, T::MaxCandidates> {
 			let now = frame_system::Pallet::<T>::block_number();
-			let kick_threshold = T::KickThreshold::get();
+			let kick_threshold = Self::kick_threshold();
 			candidates
 				.into_iter()
 				.filter_map(|c| {
@@ -472,7 +601,7 @@
 					{
 						Some(c.who)
 					} else {
-						let outcome = Self::try_remove_candidate(&c.who);
+						let outcome = Self::try_remove_candidate(&c.who, true);
 						if let Err(why) = outcome {
 							log::warn!("Failed to remove candidate {:?}", why);
 							debug_assert!(false, "failed to remove candidate {:?}", why);
modifiedpallets/collator-selection/src/mock.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/mock.rs
+++ b/pallets/collator-selection/src/mock.rs
@@ -43,7 +43,7 @@
 use sp_runtime::{
 	testing::{Header, UintAuthorityId},
 	traits::{BlakeTwo256, IdentityLookup, OpaqueKeys},
-	RuntimeAppPublic,
+	Perbill, RuntimeAppPublic,
 };
 
 type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
@@ -210,6 +210,7 @@
 	pub const MaxInvulnerables: u32 = 20;
 	pub const MinCandidates: u32 = 1;
 	pub const MaxAuthorities: u32 = 100_000;
+	pub const SlashRatio: Perbill = Perbill::one();
 }
 
 pub struct IsRegistered;
@@ -224,6 +225,7 @@
 }
 
 impl Config for Test {
+	// todo:collator mocks and stocks
 	type RuntimeEvent = RuntimeEvent;
 	type Currency = Balances;
 	type UpdateOrigin = EnsureSignedBy<RootAccount, u64>;
@@ -231,7 +233,9 @@
 	type MaxCandidates = MaxCandidates;
 	type MinCandidates = MinCandidates;
 	type MaxInvulnerables = MaxInvulnerables;
-	type KickThreshold = Period;
+	// type KickThreshold = Period;
+	type SlashRatio = SlashRatio;
+	type TreasuryAccountId = ();
 	type ValidatorId = <Self as frame_system::Config>::AccountId;
 	type ValidatorIdOf = IdentityCollator;
 	type ValidatorRegistration = IsRegistered;
@@ -240,17 +244,28 @@
 
 pub fn new_test_ext() -> sp_io::TestExternalities {
 	sp_tracing::try_init_simple();
-	let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
+	let mut t = frame_system::GenesisConfig::default()
+		.build_storage::<Test>()
+		.unwrap();
 	let invulnerables = vec![1, 2];
 
 	let balances = vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100)];
 	let keys = balances
 		.iter()
-		.map(|&(i, _)| (i, i, MockSessionKeys { aura: UintAuthorityId(i) }))
+		.map(|&(i, _)| {
+			(
+				i,
+				i,
+				MockSessionKeys {
+					aura: UintAuthorityId(i),
+				},
+			)
+		})
 		.collect::<Vec<_>>();
 	let collator_selection = collator_selection::GenesisConfig::<Test> {
 		desired_candidates: 2,
 		candidacy_bond: 10,
+		kick_threshold: 1,
 		invulnerables,
 	};
 	let session = pallet_session::GenesisConfig::<Test> { keys };
modifiedpallets/collator-selection/src/tests.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/tests.rs
+++ b/pallets/collator-selection/src/tests.rs
@@ -113,7 +113,10 @@
 		assert_eq!(CollatorSelection::candidacy_bond(), 7);
 
 		// rejects bad origin.
-		assert_noop!(CollatorSelection::set_candidacy_bond(RuntimeOrigin::signed(1), 8), BadOrigin);
+		assert_noop!(
+			CollatorSelection::set_candidacy_bond(RuntimeOrigin::signed(1), 8),
+			BadOrigin
+		);
 	});
 }
 
@@ -131,7 +134,9 @@
 
 		// reset desired candidates:
 		<crate::DesiredCandidates<Test>>::put(1);
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(4)
+		));
 
 		// but no more
 		assert_noop!(
@@ -146,7 +151,9 @@
 	new_test_ext().execute_with(|| {
 		// reset desired candidates:
 		<crate::DesiredCandidates<Test>>::put(1);
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(4)
+		));
 
 		// can not remove too few
 		assert_noop!(
@@ -184,8 +191,13 @@
 fn cannot_register_dupe_candidate() {
 	new_test_ext().execute_with(|| {
 		// can add 3 as candidate
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
-		let addition = CandidateInfo { who: 3, deposit: 10 };
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(3)
+		));
+		let addition = CandidateInfo {
+			who: 3,
+			deposit: 10,
+		};
 		assert_eq!(CollatorSelection::candidates(), vec![addition]);
 		assert_eq!(CollatorSelection::last_authored_block(3), 10);
 		assert_eq!(Balances::free_balance(3), 90);
@@ -205,7 +217,9 @@
 		assert_eq!(Balances::free_balance(&33), 0);
 
 		// works
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(3)
+		));
 
 		// poor
 		assert_noop!(
@@ -228,8 +242,12 @@
 		assert_eq!(Balances::free_balance(&3), 100);
 		assert_eq!(Balances::free_balance(&4), 100);
 
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(3)
+		));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(4)
+		));
 
 		assert_eq!(Balances::free_balance(&3), 90);
 		assert_eq!(Balances::free_balance(&4), 90);
@@ -242,11 +260,15 @@
 fn leave_intent() {
 	new_test_ext().execute_with(|| {
 		// register a candidate.
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(3)
+		));
 		assert_eq!(Balances::free_balance(3), 90);
 
 		// register too so can leave above min candidates
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(5)
+		));
 		assert_eq!(Balances::free_balance(5), 90);
 
 		// cannot leave if not candidate.
@@ -270,11 +292,16 @@
 
 		// 4 is the default author.
 		assert_eq!(Balances::free_balance(4), 100);
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(4)
+		));
 		// triggers `note_author`
 		Authorship::on_initialize(1);
 
-		let collator = CandidateInfo { who: 4, deposit: 10 };
+		let collator = CandidateInfo {
+			who: 4,
+			deposit: 10,
+		};
 
 		assert_eq!(CollatorSelection::candidates(), vec![collator]);
 		assert_eq!(CollatorSelection::last_authored_block(4), 0);
@@ -295,11 +322,16 @@
 		Balances::make_free_balance_be(&CollatorSelection::account_id(), 5);
 		// 4 is the default author.
 		assert_eq!(Balances::free_balance(4), 100);
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(4)
+		));
 		// triggers `note_author`
 		Authorship::on_initialize(1);
 
-		let collator = CandidateInfo { who: 4, deposit: 10 };
+		let collator = CandidateInfo {
+			who: 4,
+			deposit: 10,
+		};
 
 		assert_eq!(CollatorSelection::candidates(), vec![collator]);
 		assert_eq!(CollatorSelection::last_authored_block(4), 0);
@@ -324,7 +356,9 @@
 		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
 
 		// add a new collator
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(3)
+		));
 
 		// session won't see this.
 		assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
@@ -351,8 +385,12 @@
 fn kick_mechanism() {
 	new_test_ext().execute_with(|| {
 		// add a new collator
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(3)
+		));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(4)
+		));
 		initialize_to_block(10);
 		assert_eq!(CollatorSelection::candidates().len(), 2);
 		initialize_to_block(20);
@@ -361,7 +399,10 @@
 		assert_eq!(CollatorSelection::candidates().len(), 1);
 		// 3 will be kicked after 1 session delay
 		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);
-		let collator = CandidateInfo { who: 4, deposit: 10 };
+		let collator = CandidateInfo {
+			who: 4,
+			deposit: 10,
+		};
 		assert_eq!(CollatorSelection::candidates(), vec![collator]);
 		assert_eq!(CollatorSelection::last_authored_block(4), 20);
 		initialize_to_block(30);
@@ -376,8 +417,12 @@
 fn should_not_kick_mechanism_too_few() {
 	new_test_ext().execute_with(|| {
 		// add a new collator
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
-		assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(3)
+		));
+		assert_ok!(CollatorSelection::register_as_candidate(
+			RuntimeOrigin::signed(5)
+		));
 		initialize_to_block(10);
 		assert_eq!(CollatorSelection::candidates().len(), 2);
 		initialize_to_block(20);
@@ -386,7 +431,10 @@
 		assert_eq!(CollatorSelection::candidates().len(), 1);
 		// 3 will be kicked after 1 session delay
 		assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 5]);
-		let collator = CandidateInfo { who: 5, deposit: 10 };
+		let collator = CandidateInfo {
+			who: 5,
+			deposit: 10,
+		};
 		assert_eq!(CollatorSelection::candidates(), vec![collator]);
 		assert_eq!(CollatorSelection::last_authored_block(4), 20);
 		initialize_to_block(30);
@@ -401,7 +449,9 @@
 #[should_panic = "duplicate invulnerables in genesis."]
 fn cannot_set_genesis_value_twice() {
 	sp_tracing::try_init_simple();
-	let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
+	let mut t = frame_system::GenesisConfig::default()
+		.build_storage::<Test>()
+		.unwrap();
 	let invulnerables = vec![1, 1];
 
 	let collator_selection = collator_selection::GenesisConfig::<Test> {
modifiedpallets/collator-selection/src/weights.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/weights.rs
+++ b/pallets/collator-selection/src/weights.rs
@@ -41,6 +41,7 @@
 };
 use sp_std::marker::PhantomData;
 
+// todo:collator re-generate weights
 // The weight info trait for `pallet_collator_selection`.
 pub trait WeightInfo {
 	fn set_invulnerables(_b: u32) -> Weight;
modifiedprimitives/common/src/constants.rsdiffbeforeafterboth
--- a/primitives/common/src/constants.rs
+++ b/primitives/common/src/constants.rs
@@ -46,6 +46,8 @@
 pub const EXISTENTIAL_DEPOSIT: u128 = 0;
 /// Amount of Balance reserved for candidate registration.
 pub const GENESIS_CANDIDACY_BOND: u128 = EXISTENTIAL_DEPOSIT;
+/// How long a periodic session lasts in blocks.
+pub const SESSION_LENGTH: BlockNumber = MINUTES;
 
 // Targeting 0.1 UNQ per transfer
 pub const WEIGHT_TO_FEE_COEFF: u32 = /*<weight2fee>*/175_199_920/*</weight2fee>*/;
modifiedruntime/common/config/pallets/collator_selection.rsdiffbeforeafterboth
--- a/runtime/common/config/pallets/collator_selection.rs
+++ b/runtime/common/config/pallets/collator_selection.rs
@@ -18,12 +18,13 @@
 use frame_system::EnsureRoot;
 use crate::{
 	AccountId, BlockNumber, Runtime, RuntimeEvent, Balances, Aura, Session, SessionKeys,
-	CollatorSelection,
+	CollatorSelection, config::pallets::TreasuryAccountId,
 };
+use sp_runtime::Perbill;
 use up_common::constants::*;
 
 parameter_types! {
-	pub const SessionPeriod: BlockNumber = HOURS;
+	pub const SessionPeriod: BlockNumber = SESSION_LENGTH;
 	pub const SessionOffset: BlockNumber = 0;
 }
 
@@ -54,9 +55,10 @@
 
 parameter_types! {
 	pub const PotId: PalletId = PalletId(*b"PotStake");
-	pub const MaxCandidates: u32 = 1000;
-	pub const MinCandidates: u32 = 5;
-	pub const MaxInvulnerables: u32 = 100;
+	pub const MaxCandidates: u32 = 30; // todo:collator 30 collator slots - 3 planned invulnerables
+	pub const MinCandidates: u32 = 1;
+	pub const MaxInvulnerables: u32 = 30;
+	pub const SlashRatio: Perbill = Perbill::from_percent(100);
 }
 
 impl pallet_collator_selection::Config for Runtime {
@@ -64,13 +66,13 @@
 	type Currency = Balances;
 	// We allow root only to execute privileged collator selection operations.
 	type UpdateOrigin = EnsureRoot<AccountId>;
+	type TreasuryAccountId = TreasuryAccountId;
 	type PotId = PotId;
 	type MaxCandidates = MaxCandidates;
 	type MinCandidates = MinCandidates;
 	type MaxInvulnerables = MaxInvulnerables;
 	// todo:collator kick threshold should be in storage and configured only by root -- or rather UpdateOrigin
-	// Should be a multiple of session or things will get inconsistent.
-	type KickThreshold = SessionPeriod;
+	type SlashRatio = SlashRatio;
 	type ValidatorId = <Self as frame_system::Config>::AccountId;
 	type ValidatorIdOf = pallet_collator_selection::IdentityCollator;
 	type ValidatorRegistration = Session;
modifiedruntime/common/runtime_apis.rsdiffbeforeafterboth
before · runtime/common/runtime_apis.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#[macro_export]18macro_rules! dispatch_unique_runtime {19	($collection:ident.$method:ident($($name:ident),*)) => {{20		let collection = <Runtime as pallet_common::Config>::CollectionDispatch::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);21		let dispatch = collection.as_dyn();2223		Ok::<_, DispatchError>(dispatch.$method($($name),*))24	}};25}2627#[macro_export]28macro_rules! impl_common_runtime_apis {29    (30        $(31            #![custom_apis]3233            $($custom_apis:tt)+34        )?35    ) => {36        use sp_std::prelude::*;37        use sp_api::impl_runtime_apis;38        use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256, U256, H160};39        use sp_runtime::{40            Permill,41            traits::Block as BlockT,42            transaction_validity::{TransactionSource, TransactionValidity},43            ApplyExtrinsicResult, DispatchError,44        };45        use fp_rpc::TransactionStatus;46        use pallet_transaction_payment::{47            FeeDetails, RuntimeDispatchInfo,48        };49        use pallet_evm::{50            Runner, account::CrossAccountId as _,51            Account as EVMAccount, FeeCalculator,52        };53        use runtime_common::{54            sponsoring::{SponsorshipPredict, UniqueSponsorshipPredict},55            dispatch::CollectionDispatch,56            config::ethereum::CrossAccountId,57        };58        use up_data_structs::*;596061        impl_runtime_apis! {62            $($($custom_apis)+)?6364            impl up_rpc::UniqueApi<Block, CrossAccountId, AccountId> for Runtime {65                fn account_tokens(collection: CollectionId, account: CrossAccountId) -> Result<Vec<TokenId>, DispatchError> {66                    dispatch_unique_runtime!(collection.account_tokens(account))67                }68                fn collection_tokens(collection: CollectionId) -> Result<Vec<TokenId>, DispatchError> {69                    dispatch_unique_runtime!(collection.collection_tokens())70                }71                fn token_exists(collection: CollectionId, token: TokenId) -> Result<bool, DispatchError> {72                    dispatch_unique_runtime!(collection.token_exists(token))73                }7475                fn token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {76                    dispatch_unique_runtime!(collection.token_owner(token))77                }7879                fn token_owners(collection: CollectionId, token: TokenId) -> Result<Vec::<CrossAccountId>, DispatchError>  {80                   dispatch_unique_runtime!(collection.token_owners(token))81                }8283                fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {84                    let budget = up_data_structs::budget::Value::new(10);8586                    Ok(Some(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?))87                }88                fn token_children(collection: CollectionId, token: TokenId) -> Result<Vec<TokenChild>, DispatchError> {89                    Ok(<pallet_nonfungible::Pallet<Runtime>>::token_children_ids(collection, token))90                }91                fn collection_properties(92                    collection: CollectionId,93                    keys: Option<Vec<Vec<u8>>>94                ) -> Result<Vec<Property>, DispatchError> {95                    let keys = keys.map(96                        |keys| Common::bytes_keys_to_property_keys(keys)97                    ).transpose()?;9899                    Common::filter_collection_properties(collection, keys)100                }101102                fn token_properties(103                    collection: CollectionId,104                    token_id: TokenId,105                    keys: Option<Vec<Vec<u8>>>106                ) -> Result<Vec<Property>, DispatchError> {107                    let keys = keys.map(108                        |keys| Common::bytes_keys_to_property_keys(keys)109                    ).transpose()?;110111                    dispatch_unique_runtime!(collection.token_properties(token_id, keys))112                }113114                fn property_permissions(115                    collection: CollectionId,116                    keys: Option<Vec<Vec<u8>>>117                ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {118                    let keys = keys.map(119                        |keys| Common::bytes_keys_to_property_keys(keys)120                    ).transpose()?;121122                    Common::filter_property_permissions(collection, keys)123                }124125                fn token_data(126                    collection: CollectionId,127                    token_id: TokenId,128                    keys: Option<Vec<Vec<u8>>>129                ) -> Result<TokenData<CrossAccountId>, DispatchError> {130                    let token_data = TokenData {131                        properties: Self::token_properties(collection, token_id, keys)?,132                        owner: Self::token_owner(collection, token_id)?,133                        pieces: Self::total_pieces(collection, token_id)?.unwrap_or(0),134                    };135136                    Ok(token_data)137                }138139                fn total_supply(collection: CollectionId) -> Result<u32, DispatchError> {140                    dispatch_unique_runtime!(collection.total_supply())141                }142                fn account_balance(collection: CollectionId, account: CrossAccountId) -> Result<u32, DispatchError> {143                    dispatch_unique_runtime!(collection.account_balance(account))144                }145                fn balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result<u128, DispatchError> {146                    dispatch_unique_runtime!(collection.balance(account, token))147                }148                fn allowance(149                    collection: CollectionId,150                    sender: CrossAccountId,151                    spender: CrossAccountId,152                    token: TokenId,153                ) -> Result<u128, DispatchError> {154                    dispatch_unique_runtime!(collection.allowance(sender, spender, token))155                }156157                fn adminlist(collection: CollectionId) -> Result<Vec<CrossAccountId>, DispatchError> {158                    Ok(<pallet_common::Pallet<Runtime>>::adminlist(collection))159                }160                fn allowlist(collection: CollectionId) -> Result<Vec<CrossAccountId>, DispatchError> {161                    Ok(<pallet_common::Pallet<Runtime>>::allowlist(collection))162                }163                fn allowed(collection: CollectionId, user: CrossAccountId) -> Result<bool, DispatchError> {164                    Ok(<pallet_common::Pallet<Runtime>>::allowed(collection, user))165                }166                fn last_token_id(collection: CollectionId) -> Result<TokenId, DispatchError> {167                    dispatch_unique_runtime!(collection.last_token_id())168                }169                fn collection_by_id(collection: CollectionId) -> Result<Option<RpcCollection<AccountId>>, DispatchError> {170                    Ok(<pallet_common::Pallet<Runtime>>::rpc_collection(collection))171                }172                fn collection_stats() -> Result<CollectionStats, DispatchError> {173                    Ok(<pallet_common::Pallet<Runtime>>::collection_stats())174                }175                fn next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result<Option<u64>, DispatchError> {176                    Ok(<UniqueSponsorshipPredict<Runtime> as SponsorshipPredict<Runtime>>::predict(177                        collection,178                        account,179                        token180                    ))181                }182183                fn effective_collection_limits(collection: CollectionId) -> Result<Option<CollectionLimits>, DispatchError> {184                    Ok(<pallet_common::Pallet<Runtime>>::effective_collection_limits(collection))185                }186187                fn total_pieces(collection: CollectionId, token_id: TokenId) -> Result<Option<u128>, DispatchError> {188                    dispatch_unique_runtime!(collection.total_pieces(token_id))189                }190191		        fn allowance_for_all(collection: CollectionId, owner: CrossAccountId, operator: CrossAccountId) -> Result<bool, DispatchError> {192                    dispatch_unique_runtime!(collection.allowance_for_all(owner, operator))193                }194            }195196            impl app_promotion_rpc::AppPromotionApi<Block, BlockNumber, CrossAccountId, AccountId> for Runtime {197                #[allow(unused_variables)]198                fn total_staked(staker: Option<CrossAccountId>) -> Result<u128, DispatchError> {199                    #[cfg(not(feature = "app-promotion"))]200                    return unsupported!();201202                    #[cfg(feature = "app-promotion")]203                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_total_staked(staker).unwrap_or_default());204                }205206                #[allow(unused_variables)]207                fn total_staked_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>, DispatchError> {208                    #[cfg(not(feature = "app-promotion"))]209                    return unsupported!();210211                    #[cfg(feature = "app-promotion")]212                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_total_staked_per_block(staker));213                }214215                #[allow(unused_variables)]216                fn pending_unstake(staker: Option<CrossAccountId>) -> Result<u128, DispatchError> {217                    #[cfg(not(feature = "app-promotion"))]218                    return unsupported!();219220                    #[cfg(feature = "app-promotion")]221                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_pending_unstake(staker));222                }223224                #[allow(unused_variables)]225                fn pending_unstake_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>, DispatchError> {226                    #[cfg(not(feature = "app-promotion"))]227                    return unsupported!();228229                    #[cfg(feature = "app-promotion")]230                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_pending_unstake_per_block(staker))231                }232            }233234            impl rmrk_rpc::RmrkApi<235                Block,236                AccountId,237                RmrkCollectionInfo<AccountId>,238                RmrkInstanceInfo<AccountId>,239                RmrkResourceInfo,240                RmrkPropertyInfo,241                RmrkBaseInfo<AccountId>,242                RmrkPartType,243                RmrkTheme244            > for Runtime {245                fn last_collection_idx() -> Result<RmrkCollectionId, DispatchError> {246                    #[cfg(feature = "rmrk")]247                    return pallet_proxy_rmrk_core::rpc::last_collection_idx::<Runtime>();248249                    #[cfg(not(feature = "rmrk"))]250                    return unsupported!();251                }252253                #[allow(unused_variables)]254                fn collection_by_id(collection_id: RmrkCollectionId) -> Result<Option<RmrkCollectionInfo<AccountId>>, DispatchError> {255                    #[cfg(feature = "rmrk")]256                    return pallet_proxy_rmrk_core::rpc::collection_by_id::<Runtime>(collection_id);257258                    #[cfg(not(feature = "rmrk"))]259                    return unsupported!();260                }261262                #[allow(unused_variables)]263                fn nft_by_id(collection_id: RmrkCollectionId, nft_by_id: RmrkNftId) -> Result<Option<RmrkInstanceInfo<AccountId>>, DispatchError> {264                    #[cfg(feature = "rmrk")]265                    return pallet_proxy_rmrk_core::rpc::nft_by_id::<Runtime>(collection_id, nft_by_id);266267                    #[cfg(not(feature = "rmrk"))]268                    return unsupported!();269                }270271                #[allow(unused_variables)]272                fn account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Result<Vec<RmrkNftId>, DispatchError> {273                    #[cfg(feature = "rmrk")]274                    return pallet_proxy_rmrk_core::rpc::account_tokens::<Runtime>(account_id, collection_id);275276                    #[cfg(not(feature = "rmrk"))]277                    return unsupported!();278                }279280                #[allow(unused_variables)]281                fn nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkNftChild>, DispatchError> {282                    #[cfg(feature = "rmrk")]283                    return pallet_proxy_rmrk_core::rpc::nft_children::<Runtime>(collection_id, nft_id);284285                    #[cfg(not(feature = "rmrk"))]286                    return unsupported!();287                }288289                #[allow(unused_variables)]290                fn collection_properties(291                    collection_id: RmrkCollectionId,292                    filter_keys: Option<Vec<RmrkPropertyKey>>293                ) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {294                    #[cfg(feature = "rmrk")]295                    return pallet_proxy_rmrk_core::rpc::collection_properties::<Runtime>(collection_id, filter_keys);296297                    #[cfg(not(feature = "rmrk"))]298                    return unsupported!();299                }300301                #[allow(unused_variables)]302                fn nft_properties(303                    collection_id: RmrkCollectionId,304                    nft_id: RmrkNftId,305                    filter_keys: Option<Vec<RmrkPropertyKey>>306                ) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {307                    #[cfg(feature = "rmrk")]308                    return pallet_proxy_rmrk_core::rpc::nft_properties::<Runtime>(collection_id, nft_id, filter_keys);309310                    #[cfg(not(feature = "rmrk"))]311                    return unsupported!();312                }313314                #[allow(unused_variables)]315                fn nft_resources(collection_id: RmrkCollectionId,nft_id: RmrkNftId) -> Result<Vec<RmrkResourceInfo>, DispatchError> {316                    #[cfg(feature = "rmrk")]317                    return pallet_proxy_rmrk_core::rpc::nft_resources::<Runtime>(collection_id, nft_id);318319                    #[cfg(not(feature = "rmrk"))]320                    return unsupported!();321                }322323                #[allow(unused_variables)]324                fn nft_resource_priority(325                    collection_id: RmrkCollectionId,326                    nft_id: RmrkNftId,327                    resource_id: RmrkResourceId328                ) -> Result<Option<u32>, DispatchError> {329                    #[cfg(feature = "rmrk")]330                    return pallet_proxy_rmrk_core::rpc::nft_resource_priority::<Runtime>(collection_id, nft_id, resource_id);331332                    #[cfg(not(feature = "rmrk"))]333                    return unsupported!();334                }335336                #[allow(unused_variables)]337                fn base(base_id: RmrkBaseId) -> Result<Option<RmrkBaseInfo<AccountId>>, DispatchError> {338                    #[cfg(feature = "rmrk")]339                    return pallet_proxy_rmrk_equip::rpc::base::<Runtime>(base_id);340341                    #[cfg(not(feature = "rmrk"))]342                    return unsupported!();343                }344345                #[allow(unused_variables)]346                fn base_parts(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {347                    #[cfg(feature = "rmrk")]348                    return pallet_proxy_rmrk_equip::rpc::base_parts::<Runtime>(base_id);349350                    #[cfg(not(feature = "rmrk"))]351                    return unsupported!();352                }353354                #[allow(unused_variables)]355                fn theme_names(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {356                    #[cfg(feature = "rmrk")]357                    return pallet_proxy_rmrk_equip::rpc::theme_names::<Runtime>(base_id);358359                    #[cfg(not(feature = "rmrk"))]360                    return unsupported!();361                }362363                #[allow(unused_variables)]364                fn theme(365                    base_id: RmrkBaseId,366                    theme_name: RmrkThemeName,367                    filter_keys: Option<Vec<RmrkPropertyKey>>368                ) -> Result<Option<RmrkTheme>, DispatchError> {369                    #[cfg(feature = "rmrk")]370                    return pallet_proxy_rmrk_equip::rpc::theme::<Runtime>(base_id, theme_name, filter_keys);371372                    #[cfg(not(feature = "rmrk"))]373                    return unsupported!();374                }375            }376377            impl sp_api::Core<Block> for Runtime {378                fn version() -> RuntimeVersion {379                    VERSION380                }381382                fn execute_block(block: Block) {383                    Executive::execute_block(block)384                }385386                fn initialize_block(header: &<Block as BlockT>::Header) {387                    Executive::initialize_block(header)388                }389            }390391            impl sp_api::Metadata<Block> for Runtime {392                fn metadata() -> OpaqueMetadata {393                    OpaqueMetadata::new(Runtime::metadata().into())394                }395            }396397            impl sp_block_builder::BlockBuilder<Block> for Runtime {398                fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {399                    Executive::apply_extrinsic(extrinsic)400                }401402                fn finalize_block() -> <Block as BlockT>::Header {403                    Executive::finalize_block()404                }405406                fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {407                    data.create_extrinsics()408                }409410                fn check_inherents(411                    block: Block,412                    data: sp_inherents::InherentData,413                ) -> sp_inherents::CheckInherentsResult {414                    data.check_extrinsics(&block)415                }416417                // fn random_seed() -> <Block as BlockT>::Hash {418                //     RandomnessCollectiveFlip::random_seed().0419                // }420            }421422            impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {423                fn validate_transaction(424                    source: TransactionSource,425                    tx: <Block as BlockT>::Extrinsic,426                    hash: <Block as BlockT>::Hash,427                ) -> TransactionValidity {428                    Executive::validate_transaction(source, tx, hash)429                }430            }431432            impl sp_offchain::OffchainWorkerApi<Block> for Runtime {433                fn offchain_worker(header: &<Block as BlockT>::Header) {434                    Executive::offchain_worker(header)435                }436            }437438            impl fp_rpc::EthereumRuntimeRPCApi<Block> for Runtime {439                fn chain_id() -> u64 {440                    <Runtime as pallet_evm::Config>::ChainId::get()441                }442443                fn account_basic(address: H160) -> EVMAccount {444                    let (account, _) = EVM::account_basic(&address);445                    account446                }447448                fn gas_price() -> U256 {449                    let (price, _) = <Runtime as pallet_evm::Config>::FeeCalculator::min_gas_price();450                    price451                }452453                fn account_code_at(address: H160) -> Vec<u8> {454                    EVM::account_codes(address)455                }456457                fn author() -> H160 {458                    <pallet_evm::Pallet<Runtime>>::find_author()459                }460461                fn storage_at(address: H160, index: U256) -> H256 {462                    let mut tmp = [0u8; 32];463                    index.to_big_endian(&mut tmp);464                    EVM::account_storages(address, H256::from_slice(&tmp[..]))465                }466467                #[allow(clippy::redundant_closure)]468                fn call(469                    from: H160,470                    to: H160,471                    data: Vec<u8>,472                    value: U256,473                    gas_limit: U256,474                    max_fee_per_gas: Option<U256>,475                    max_priority_fee_per_gas: Option<U256>,476                    nonce: Option<U256>,477                    estimate: bool,478                    access_list: Option<Vec<(H160, Vec<H256>)>>,479                ) -> Result<pallet_evm::CallInfo, sp_runtime::DispatchError> {480                    let config = if estimate {481                        let mut config = <Runtime as pallet_evm::Config>::config().clone();482                        config.estimate = true;483                        Some(config)484                    } else {485                        None486                    };487488                    let is_transactional = false;489                    let validate = false;490                    <Runtime as pallet_evm::Config>::Runner::call(491                        CrossAccountId::from_eth(from),492                        to,493                        data,494                        value,495                        gas_limit.low_u64(),496                        max_fee_per_gas,497                        max_priority_fee_per_gas,498                        nonce,499                        access_list.unwrap_or_default(),500                        is_transactional,501                        validate,502                        config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),503                    ).map_err(|err| err.error.into())504                }505506                #[allow(clippy::redundant_closure)]507                fn create(508                    from: H160,509                    data: Vec<u8>,510                    value: U256,511                    gas_limit: U256,512                    max_fee_per_gas: Option<U256>,513                    max_priority_fee_per_gas: Option<U256>,514                    nonce: Option<U256>,515                    estimate: bool,516                    access_list: Option<Vec<(H160, Vec<H256>)>>,517                ) -> Result<pallet_evm::CreateInfo, sp_runtime::DispatchError> {518                    let config = if estimate {519                        let mut config = <Runtime as pallet_evm::Config>::config().clone();520                        config.estimate = true;521                        Some(config)522                    } else {523                        None524                    };525526                    let is_transactional = false;527                    let validate = false;528                    <Runtime as pallet_evm::Config>::Runner::create(529                        CrossAccountId::from_eth(from),530                        data,531                        value,532                        gas_limit.low_u64(),533                        max_fee_per_gas,534                        max_priority_fee_per_gas,535                        nonce,536                        access_list.unwrap_or_default(),537                        is_transactional,538                        validate,539                        config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),540                    ).map_err(|err| err.error.into())541                }542543                fn current_transaction_statuses() -> Option<Vec<TransactionStatus>> {544                    Ethereum::current_transaction_statuses()545                }546547                fn current_block() -> Option<pallet_ethereum::Block> {548                    Ethereum::current_block()549                }550551                fn current_receipts() -> Option<Vec<pallet_ethereum::Receipt>> {552                    Ethereum::current_receipts()553                }554555                fn current_all() -> (556                    Option<pallet_ethereum::Block>,557                    Option<Vec<pallet_ethereum::Receipt>>,558                    Option<Vec<TransactionStatus>>559                ) {560                    (561                        Ethereum::current_block(),562                        Ethereum::current_receipts(),563                        Ethereum::current_transaction_statuses()564                    )565                }566567                fn extrinsic_filter(xts: Vec<<Block as sp_api::BlockT>::Extrinsic>) -> Vec<pallet_ethereum::Transaction> {568                    xts.into_iter().filter_map(|xt| match xt.0.function {569                        RuntimeCall::Ethereum(pallet_ethereum::Call::transact { transaction }) => Some(transaction),570                        _ => None571                    }).collect()572                }573574                fn elasticity() -> Option<Permill> {575                    None576                }577            }578579            impl fp_rpc::ConvertTransactionRuntimeApi<Block> for Runtime {580                fn convert_transaction(transaction: pallet_ethereum::Transaction) -> <Block as BlockT>::Extrinsic  {581                    UncheckedExtrinsic::new_unsigned(582                        pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),583                    )584                }585            }586587            impl sp_session::SessionKeys<Block> for Runtime {588                fn decode_session_keys(589                    encoded: Vec<u8>,590                ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {591                    SessionKeys::decode_into_raw_public_keys(&encoded)592                }593594                fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {595                    SessionKeys::generate(seed)596                }597            }598599            impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {600                fn slot_duration() -> sp_consensus_aura::SlotDuration {601                    sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration())602                }603604                fn authorities() -> Vec<AuraId> {605                    Aura::authorities().to_vec()606                }607            }608609            impl cumulus_primitives_core::CollectCollationInfo<Block> for Runtime {610                fn collect_collation_info(header: &<Block as BlockT>::Header) -> cumulus_primitives_core::CollationInfo {611                    ParachainSystem::collect_collation_info(header)612                }613            }614615            impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {616                fn account_nonce(account: AccountId) -> Index {617                    System::account_nonce(account)618                }619            }620621            impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {622                fn query_info(uxt: <Block as BlockT>::Extrinsic, len: u32) -> RuntimeDispatchInfo<Balance> {623                    TransactionPayment::query_info(uxt, len)624                }625                fn query_fee_details(uxt: <Block as BlockT>::Extrinsic, len: u32) -> FeeDetails<Balance> {626                    TransactionPayment::query_fee_details(uxt, len)627                }628            }629630            /*631            impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber, Hash>632                for Runtime633            {634                fn call(635                    origin: AccountId,636                    dest: AccountId,637                    value: Balance,638                    gas_limit: u64,639                    input_data: Vec<u8>,640                ) -> pallet_contracts_primitives::ContractExecResult {641                    Contracts::bare_call(origin, dest, value, gas_limit, input_data, false)642                }643644                fn instantiate(645                    origin: AccountId,646                    endowment: Balance,647                    gas_limit: u64,648                    code: pallet_contracts_primitives::Code<Hash>,649                    data: Vec<u8>,650                    salt: Vec<u8>,651                ) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, BlockNumber>652                {653                    Contracts::bare_instantiate(origin, endowment, gas_limit, code, data, salt, true, false)654                }655656                fn get_storage(657                    address: AccountId,658                    key: [u8; 32],659                ) -> pallet_contracts_primitives::GetStorageResult {660                    Contracts::get_storage(address, key)661                }662663                fn rent_projection(664                    address: AccountId,665                ) -> pallet_contracts_primitives::RentProjectionResult<BlockNumber> {666                    Contracts::rent_projection(address)667                }668            }669            */670671            #[cfg(feature = "runtime-benchmarks")]672            impl frame_benchmarking::Benchmark<Block> for Runtime {673                fn benchmark_metadata(extra: bool) -> (674                    Vec<frame_benchmarking::BenchmarkList>,675                    Vec<frame_support::traits::StorageInfo>,676                ) {677                    use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList};678                    use frame_support::traits::StorageInfoTrait;679680                    let mut list = Vec::<BenchmarkList>::new();681682                    list_benchmark!(list, extra, pallet_evm_migration, EvmMigration);683                    list_benchmark!(list, extra, pallet_common, Common);684                    list_benchmark!(list, extra, pallet_unique, Unique);685                    list_benchmark!(list, extra, pallet_structure, Structure);686                    list_benchmark!(list, extra, pallet_inflation, Inflation);687688                    #[cfg(feature = "app-promotion")]689                    list_benchmark!(list, extra, pallet_app_promotion, AppPromotion);690691                    list_benchmark!(list, extra, pallet_fungible, Fungible);692                    list_benchmark!(list, extra, pallet_nonfungible, Nonfungible);693694                    #[cfg(feature = "refungible")]695                    list_benchmark!(list, extra, pallet_refungible, Refungible);696697                    #[cfg(feature = "scheduler")]698                    list_benchmark!(list, extra, pallet_unique_scheduler_v2, Scheduler);699700                    #[cfg(feature = "rmrk")]701                    list_benchmark!(list, extra, pallet_proxy_rmrk_core, RmrkCore);702703                    #[cfg(feature = "rmrk")]704                    list_benchmark!(list, extra, pallet_proxy_rmrk_equip, RmrkEquip);705706                    #[cfg(feature = "foreign-assets")]707                    list_benchmark!(list, extra, pallet_foreign_assets, ForeignAssets);708709710                    // list_benchmark!(list, extra, pallet_evm_coder_substrate, EvmCoderSubstrate);711712                    let storage_info = AllPalletsReversedWithSystemFirst::storage_info();713714                    return (list, storage_info)715                }716717                fn dispatch_benchmark(718                    config: frame_benchmarking::BenchmarkConfig719                ) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {720                    use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};721722                    let allowlist: Vec<TrackedStorageKey> = vec![723                        // Total Issuance724                        hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),725726                        // Block Number727                        hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),728                        // Execution Phase729                        hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),730                        // Event Count731                        hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),732                        // System Events733                        hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),734735                        // Evm CurrentLogs736                        hex_literal::hex!("1da53b775b270400e7e61ed5cbc5a146547f210cec367e9af919603343b9cb56").to_vec().into(),737738                        // Transactional depth739                        hex_literal::hex!("3a7472616e73616374696f6e5f6c6576656c3a").to_vec().into(),740                    ];741742                    let mut batches = Vec::<BenchmarkBatch>::new();743                    let params = (&config, &allowlist);744745                    add_benchmark!(params, batches, pallet_evm_migration, EvmMigration);746                    add_benchmark!(params, batches, pallet_common, Common);747                    add_benchmark!(params, batches, pallet_unique, Unique);748                    add_benchmark!(params, batches, pallet_structure, Structure);749                    add_benchmark!(params, batches, pallet_inflation, Inflation);750751                    #[cfg(feature = "app-promotion")]752                    add_benchmark!(params, batches, pallet_app_promotion, AppPromotion);753754                    add_benchmark!(params, batches, pallet_fungible, Fungible);755                    add_benchmark!(params, batches, pallet_nonfungible, Nonfungible);756757                    #[cfg(feature = "refungible")]758                    add_benchmark!(params, batches, pallet_refungible, Refungible);759760                    #[cfg(feature = "scheduler")]761                    add_benchmark!(params, batches, pallet_unique_scheduler_v2, Scheduler);762763                    #[cfg(feature = "rmrk")]764                    add_benchmark!(params, batches, pallet_proxy_rmrk_core, RmrkCore);765766                    #[cfg(feature = "rmrk")]767                    add_benchmark!(params, batches, pallet_proxy_rmrk_equip, RmrkEquip);768769                    #[cfg(feature = "foreign-assets")]770                    add_benchmark!(params, batches, pallet_foreign_assets, ForeignAssets);771772                    // add_benchmark!(params, batches, pallet_evm_coder_substrate, EvmCoderSubstrate);773774                    if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }775                    Ok(batches)776                }777            }778779            #[cfg(feature = "try-runtime")]780            impl frame_try_runtime::TryRuntime<Block> for Runtime {781                fn on_runtime_upgrade() -> (frame_support::pallet_prelude::Weight, frame_support::pallet_prelude::Weight) {782                    log::info!("try-runtime::on_runtime_upgrade unique-chain.");783                    let weight = Executive::try_runtime_upgrade().unwrap();784                    (weight, crate::config::substrate::RuntimeBlockWeights::get().max_block)785                }786787                fn execute_block(788                    block: Block,789                    state_root_check: bool,790                    select: frame_try_runtime::TryStateSelect791                ) -> frame_support::pallet_prelude::Weight {792                    log::info!(793                        target: "node-runtime",794                        "try-runtime: executing block {:?} / root checks: {:?} / try-state-select: {:?}",795                        block.header.hash(),796                        state_root_check,797                        select,798                    );799800                    Executive::try_execute_block(block, state_root_check, select).unwrap()801                }802            }803        }804    }805}
after · runtime/common/runtime_apis.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#[macro_export]18macro_rules! dispatch_unique_runtime {19	($collection:ident.$method:ident($($name:ident),*)) => {{20		let collection = <Runtime as pallet_common::Config>::CollectionDispatch::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);21		let dispatch = collection.as_dyn();2223		Ok::<_, DispatchError>(dispatch.$method($($name),*))24	}};25}2627#[macro_export]28macro_rules! impl_common_runtime_apis {29    (30        $(31            #![custom_apis]3233            $($custom_apis:tt)+34        )?35    ) => {36        use sp_std::prelude::*;37        use sp_api::impl_runtime_apis;38        use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256, U256, H160};39        use sp_runtime::{40            Permill,41            traits::Block as BlockT,42            transaction_validity::{TransactionSource, TransactionValidity},43            ApplyExtrinsicResult, DispatchError,44        };45        use fp_rpc::TransactionStatus;46        use pallet_transaction_payment::{47            FeeDetails, RuntimeDispatchInfo,48        };49        use pallet_evm::{50            Runner, account::CrossAccountId as _,51            Account as EVMAccount, FeeCalculator,52        };53        use runtime_common::{54            sponsoring::{SponsorshipPredict, UniqueSponsorshipPredict},55            dispatch::CollectionDispatch,56            config::ethereum::CrossAccountId,57        };58        use up_data_structs::*;596061        impl_runtime_apis! {62            $($($custom_apis)+)?6364            impl up_rpc::UniqueApi<Block, CrossAccountId, AccountId> for Runtime {65                fn account_tokens(collection: CollectionId, account: CrossAccountId) -> Result<Vec<TokenId>, DispatchError> {66                    dispatch_unique_runtime!(collection.account_tokens(account))67                }68                fn collection_tokens(collection: CollectionId) -> Result<Vec<TokenId>, DispatchError> {69                    dispatch_unique_runtime!(collection.collection_tokens())70                }71                fn token_exists(collection: CollectionId, token: TokenId) -> Result<bool, DispatchError> {72                    dispatch_unique_runtime!(collection.token_exists(token))73                }7475                fn token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {76                    dispatch_unique_runtime!(collection.token_owner(token))77                }7879                fn token_owners(collection: CollectionId, token: TokenId) -> Result<Vec::<CrossAccountId>, DispatchError>  {80                   dispatch_unique_runtime!(collection.token_owners(token))81                }8283                fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {84                    let budget = up_data_structs::budget::Value::new(10);8586                    Ok(Some(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?))87                }88                fn token_children(collection: CollectionId, token: TokenId) -> Result<Vec<TokenChild>, DispatchError> {89                    Ok(<pallet_nonfungible::Pallet<Runtime>>::token_children_ids(collection, token))90                }91                fn collection_properties(92                    collection: CollectionId,93                    keys: Option<Vec<Vec<u8>>>94                ) -> Result<Vec<Property>, DispatchError> {95                    let keys = keys.map(96                        |keys| Common::bytes_keys_to_property_keys(keys)97                    ).transpose()?;9899                    Common::filter_collection_properties(collection, keys)100                }101102                fn token_properties(103                    collection: CollectionId,104                    token_id: TokenId,105                    keys: Option<Vec<Vec<u8>>>106                ) -> Result<Vec<Property>, DispatchError> {107                    let keys = keys.map(108                        |keys| Common::bytes_keys_to_property_keys(keys)109                    ).transpose()?;110111                    dispatch_unique_runtime!(collection.token_properties(token_id, keys))112                }113114                fn property_permissions(115                    collection: CollectionId,116                    keys: Option<Vec<Vec<u8>>>117                ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {118                    let keys = keys.map(119                        |keys| Common::bytes_keys_to_property_keys(keys)120                    ).transpose()?;121122                    Common::filter_property_permissions(collection, keys)123                }124125                fn token_data(126                    collection: CollectionId,127                    token_id: TokenId,128                    keys: Option<Vec<Vec<u8>>>129                ) -> Result<TokenData<CrossAccountId>, DispatchError> {130                    let token_data = TokenData {131                        properties: Self::token_properties(collection, token_id, keys)?,132                        owner: Self::token_owner(collection, token_id)?,133                        pieces: Self::total_pieces(collection, token_id)?.unwrap_or(0),134                    };135136                    Ok(token_data)137                }138139                fn total_supply(collection: CollectionId) -> Result<u32, DispatchError> {140                    dispatch_unique_runtime!(collection.total_supply())141                }142                fn account_balance(collection: CollectionId, account: CrossAccountId) -> Result<u32, DispatchError> {143                    dispatch_unique_runtime!(collection.account_balance(account))144                }145                fn balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result<u128, DispatchError> {146                    dispatch_unique_runtime!(collection.balance(account, token))147                }148                fn allowance(149                    collection: CollectionId,150                    sender: CrossAccountId,151                    spender: CrossAccountId,152                    token: TokenId,153                ) -> Result<u128, DispatchError> {154                    dispatch_unique_runtime!(collection.allowance(sender, spender, token))155                }156157                fn adminlist(collection: CollectionId) -> Result<Vec<CrossAccountId>, DispatchError> {158                    Ok(<pallet_common::Pallet<Runtime>>::adminlist(collection))159                }160                fn allowlist(collection: CollectionId) -> Result<Vec<CrossAccountId>, DispatchError> {161                    Ok(<pallet_common::Pallet<Runtime>>::allowlist(collection))162                }163                fn allowed(collection: CollectionId, user: CrossAccountId) -> Result<bool, DispatchError> {164                    Ok(<pallet_common::Pallet<Runtime>>::allowed(collection, user))165                }166                fn last_token_id(collection: CollectionId) -> Result<TokenId, DispatchError> {167                    dispatch_unique_runtime!(collection.last_token_id())168                }169                fn collection_by_id(collection: CollectionId) -> Result<Option<RpcCollection<AccountId>>, DispatchError> {170                    Ok(<pallet_common::Pallet<Runtime>>::rpc_collection(collection))171                }172                fn collection_stats() -> Result<CollectionStats, DispatchError> {173                    Ok(<pallet_common::Pallet<Runtime>>::collection_stats())174                }175                fn next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result<Option<u64>, DispatchError> {176                    Ok(<UniqueSponsorshipPredict<Runtime> as SponsorshipPredict<Runtime>>::predict(177                        collection,178                        account,179                        token180                    ))181                }182183                fn effective_collection_limits(collection: CollectionId) -> Result<Option<CollectionLimits>, DispatchError> {184                    Ok(<pallet_common::Pallet<Runtime>>::effective_collection_limits(collection))185                }186187                fn total_pieces(collection: CollectionId, token_id: TokenId) -> Result<Option<u128>, DispatchError> {188                    dispatch_unique_runtime!(collection.total_pieces(token_id))189                }190191		        fn allowance_for_all(collection: CollectionId, owner: CrossAccountId, operator: CrossAccountId) -> Result<bool, DispatchError> {192                    dispatch_unique_runtime!(collection.allowance_for_all(owner, operator))193                }194            }195196            impl app_promotion_rpc::AppPromotionApi<Block, BlockNumber, CrossAccountId, AccountId> for Runtime {197                #[allow(unused_variables)]198                fn total_staked(staker: Option<CrossAccountId>) -> Result<u128, DispatchError> {199                    #[cfg(not(feature = "app-promotion"))]200                    return unsupported!();201202                    #[cfg(feature = "app-promotion")]203                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_total_staked(staker).unwrap_or_default());204                }205206                #[allow(unused_variables)]207                fn total_staked_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>, DispatchError> {208                    #[cfg(not(feature = "app-promotion"))]209                    return unsupported!();210211                    #[cfg(feature = "app-promotion")]212                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_total_staked_per_block(staker));213                }214215                #[allow(unused_variables)]216                fn pending_unstake(staker: Option<CrossAccountId>) -> Result<u128, DispatchError> {217                    #[cfg(not(feature = "app-promotion"))]218                    return unsupported!();219220                    #[cfg(feature = "app-promotion")]221                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_pending_unstake(staker));222                }223224                #[allow(unused_variables)]225                fn pending_unstake_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>, DispatchError> {226                    #[cfg(not(feature = "app-promotion"))]227                    return unsupported!();228229                    #[cfg(feature = "app-promotion")]230                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_pending_unstake_per_block(staker))231                }232            }233234            impl rmrk_rpc::RmrkApi<235                Block,236                AccountId,237                RmrkCollectionInfo<AccountId>,238                RmrkInstanceInfo<AccountId>,239                RmrkResourceInfo,240                RmrkPropertyInfo,241                RmrkBaseInfo<AccountId>,242                RmrkPartType,243                RmrkTheme244            > for Runtime {245                fn last_collection_idx() -> Result<RmrkCollectionId, DispatchError> {246                    #[cfg(feature = "rmrk")]247                    return pallet_proxy_rmrk_core::rpc::last_collection_idx::<Runtime>();248249                    #[cfg(not(feature = "rmrk"))]250                    return unsupported!();251                }252253                #[allow(unused_variables)]254                fn collection_by_id(collection_id: RmrkCollectionId) -> Result<Option<RmrkCollectionInfo<AccountId>>, DispatchError> {255                    #[cfg(feature = "rmrk")]256                    return pallet_proxy_rmrk_core::rpc::collection_by_id::<Runtime>(collection_id);257258                    #[cfg(not(feature = "rmrk"))]259                    return unsupported!();260                }261262                #[allow(unused_variables)]263                fn nft_by_id(collection_id: RmrkCollectionId, nft_by_id: RmrkNftId) -> Result<Option<RmrkInstanceInfo<AccountId>>, DispatchError> {264                    #[cfg(feature = "rmrk")]265                    return pallet_proxy_rmrk_core::rpc::nft_by_id::<Runtime>(collection_id, nft_by_id);266267                    #[cfg(not(feature = "rmrk"))]268                    return unsupported!();269                }270271                #[allow(unused_variables)]272                fn account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Result<Vec<RmrkNftId>, DispatchError> {273                    #[cfg(feature = "rmrk")]274                    return pallet_proxy_rmrk_core::rpc::account_tokens::<Runtime>(account_id, collection_id);275276                    #[cfg(not(feature = "rmrk"))]277                    return unsupported!();278                }279280                #[allow(unused_variables)]281                fn nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkNftChild>, DispatchError> {282                    #[cfg(feature = "rmrk")]283                    return pallet_proxy_rmrk_core::rpc::nft_children::<Runtime>(collection_id, nft_id);284285                    #[cfg(not(feature = "rmrk"))]286                    return unsupported!();287                }288289                #[allow(unused_variables)]290                fn collection_properties(291                    collection_id: RmrkCollectionId,292                    filter_keys: Option<Vec<RmrkPropertyKey>>293                ) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {294                    #[cfg(feature = "rmrk")]295                    return pallet_proxy_rmrk_core::rpc::collection_properties::<Runtime>(collection_id, filter_keys);296297                    #[cfg(not(feature = "rmrk"))]298                    return unsupported!();299                }300301                #[allow(unused_variables)]302                fn nft_properties(303                    collection_id: RmrkCollectionId,304                    nft_id: RmrkNftId,305                    filter_keys: Option<Vec<RmrkPropertyKey>>306                ) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {307                    #[cfg(feature = "rmrk")]308                    return pallet_proxy_rmrk_core::rpc::nft_properties::<Runtime>(collection_id, nft_id, filter_keys);309310                    #[cfg(not(feature = "rmrk"))]311                    return unsupported!();312                }313314                #[allow(unused_variables)]315                fn nft_resources(collection_id: RmrkCollectionId,nft_id: RmrkNftId) -> Result<Vec<RmrkResourceInfo>, DispatchError> {316                    #[cfg(feature = "rmrk")]317                    return pallet_proxy_rmrk_core::rpc::nft_resources::<Runtime>(collection_id, nft_id);318319                    #[cfg(not(feature = "rmrk"))]320                    return unsupported!();321                }322323                #[allow(unused_variables)]324                fn nft_resource_priority(325                    collection_id: RmrkCollectionId,326                    nft_id: RmrkNftId,327                    resource_id: RmrkResourceId328                ) -> Result<Option<u32>, DispatchError> {329                    #[cfg(feature = "rmrk")]330                    return pallet_proxy_rmrk_core::rpc::nft_resource_priority::<Runtime>(collection_id, nft_id, resource_id);331332                    #[cfg(not(feature = "rmrk"))]333                    return unsupported!();334                }335336                #[allow(unused_variables)]337                fn base(base_id: RmrkBaseId) -> Result<Option<RmrkBaseInfo<AccountId>>, DispatchError> {338                    #[cfg(feature = "rmrk")]339                    return pallet_proxy_rmrk_equip::rpc::base::<Runtime>(base_id);340341                    #[cfg(not(feature = "rmrk"))]342                    return unsupported!();343                }344345                #[allow(unused_variables)]346                fn base_parts(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {347                    #[cfg(feature = "rmrk")]348                    return pallet_proxy_rmrk_equip::rpc::base_parts::<Runtime>(base_id);349350                    #[cfg(not(feature = "rmrk"))]351                    return unsupported!();352                }353354                #[allow(unused_variables)]355                fn theme_names(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {356                    #[cfg(feature = "rmrk")]357                    return pallet_proxy_rmrk_equip::rpc::theme_names::<Runtime>(base_id);358359                    #[cfg(not(feature = "rmrk"))]360                    return unsupported!();361                }362363                #[allow(unused_variables)]364                fn theme(365                    base_id: RmrkBaseId,366                    theme_name: RmrkThemeName,367                    filter_keys: Option<Vec<RmrkPropertyKey>>368                ) -> Result<Option<RmrkTheme>, DispatchError> {369                    #[cfg(feature = "rmrk")]370                    return pallet_proxy_rmrk_equip::rpc::theme::<Runtime>(base_id, theme_name, filter_keys);371372                    #[cfg(not(feature = "rmrk"))]373                    return unsupported!();374                }375            }376377            impl sp_api::Core<Block> for Runtime {378                fn version() -> RuntimeVersion {379                    VERSION380                }381382                fn execute_block(block: Block) {383                    Executive::execute_block(block)384                }385386                fn initialize_block(header: &<Block as BlockT>::Header) {387                    Executive::initialize_block(header)388                }389            }390391            impl sp_api::Metadata<Block> for Runtime {392                fn metadata() -> OpaqueMetadata {393                    OpaqueMetadata::new(Runtime::metadata().into())394                }395            }396397            impl sp_block_builder::BlockBuilder<Block> for Runtime {398                fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {399                    Executive::apply_extrinsic(extrinsic)400                }401402                fn finalize_block() -> <Block as BlockT>::Header {403                    Executive::finalize_block()404                }405406                fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {407                    data.create_extrinsics()408                }409410                fn check_inherents(411                    block: Block,412                    data: sp_inherents::InherentData,413                ) -> sp_inherents::CheckInherentsResult {414                    data.check_extrinsics(&block)415                }416417                // fn random_seed() -> <Block as BlockT>::Hash {418                //     RandomnessCollectiveFlip::random_seed().0419                // }420            }421422            impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {423                fn validate_transaction(424                    source: TransactionSource,425                    tx: <Block as BlockT>::Extrinsic,426                    hash: <Block as BlockT>::Hash,427                ) -> TransactionValidity {428                    Executive::validate_transaction(source, tx, hash)429                }430            }431432            impl sp_offchain::OffchainWorkerApi<Block> for Runtime {433                fn offchain_worker(header: &<Block as BlockT>::Header) {434                    Executive::offchain_worker(header)435                }436            }437438            impl fp_rpc::EthereumRuntimeRPCApi<Block> for Runtime {439                fn chain_id() -> u64 {440                    <Runtime as pallet_evm::Config>::ChainId::get()441                }442443                fn account_basic(address: H160) -> EVMAccount {444                    let (account, _) = EVM::account_basic(&address);445                    account446                }447448                fn gas_price() -> U256 {449                    let (price, _) = <Runtime as pallet_evm::Config>::FeeCalculator::min_gas_price();450                    price451                }452453                fn account_code_at(address: H160) -> Vec<u8> {454                    EVM::account_codes(address)455                }456457                fn author() -> H160 {458                    <pallet_evm::Pallet<Runtime>>::find_author()459                }460461                fn storage_at(address: H160, index: U256) -> H256 {462                    let mut tmp = [0u8; 32];463                    index.to_big_endian(&mut tmp);464                    EVM::account_storages(address, H256::from_slice(&tmp[..]))465                }466467                #[allow(clippy::redundant_closure)]468                fn call(469                    from: H160,470                    to: H160,471                    data: Vec<u8>,472                    value: U256,473                    gas_limit: U256,474                    max_fee_per_gas: Option<U256>,475                    max_priority_fee_per_gas: Option<U256>,476                    nonce: Option<U256>,477                    estimate: bool,478                    access_list: Option<Vec<(H160, Vec<H256>)>>,479                ) -> Result<pallet_evm::CallInfo, sp_runtime::DispatchError> {480                    let config = if estimate {481                        let mut config = <Runtime as pallet_evm::Config>::config().clone();482                        config.estimate = true;483                        Some(config)484                    } else {485                        None486                    };487488                    let is_transactional = false;489                    let validate = false;490                    <Runtime as pallet_evm::Config>::Runner::call(491                        CrossAccountId::from_eth(from),492                        to,493                        data,494                        value,495                        gas_limit.low_u64(),496                        max_fee_per_gas,497                        max_priority_fee_per_gas,498                        nonce,499                        access_list.unwrap_or_default(),500                        is_transactional,501                        validate,502                        config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),503                    ).map_err(|err| err.error.into())504                }505506                #[allow(clippy::redundant_closure)]507                fn create(508                    from: H160,509                    data: Vec<u8>,510                    value: U256,511                    gas_limit: U256,512                    max_fee_per_gas: Option<U256>,513                    max_priority_fee_per_gas: Option<U256>,514                    nonce: Option<U256>,515                    estimate: bool,516                    access_list: Option<Vec<(H160, Vec<H256>)>>,517                ) -> Result<pallet_evm::CreateInfo, sp_runtime::DispatchError> {518                    let config = if estimate {519                        let mut config = <Runtime as pallet_evm::Config>::config().clone();520                        config.estimate = true;521                        Some(config)522                    } else {523                        None524                    };525526                    let is_transactional = false;527                    let validate = false;528                    <Runtime as pallet_evm::Config>::Runner::create(529                        CrossAccountId::from_eth(from),530                        data,531                        value,532                        gas_limit.low_u64(),533                        max_fee_per_gas,534                        max_priority_fee_per_gas,535                        nonce,536                        access_list.unwrap_or_default(),537                        is_transactional,538                        validate,539                        config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),540                    ).map_err(|err| err.error.into())541                }542543                fn current_transaction_statuses() -> Option<Vec<TransactionStatus>> {544                    Ethereum::current_transaction_statuses()545                }546547                fn current_block() -> Option<pallet_ethereum::Block> {548                    Ethereum::current_block()549                }550551                fn current_receipts() -> Option<Vec<pallet_ethereum::Receipt>> {552                    Ethereum::current_receipts()553                }554555                fn current_all() -> (556                    Option<pallet_ethereum::Block>,557                    Option<Vec<pallet_ethereum::Receipt>>,558                    Option<Vec<TransactionStatus>>559                ) {560                    (561                        Ethereum::current_block(),562                        Ethereum::current_receipts(),563                        Ethereum::current_transaction_statuses()564                    )565                }566567                fn extrinsic_filter(xts: Vec<<Block as sp_api::BlockT>::Extrinsic>) -> Vec<pallet_ethereum::Transaction> {568                    xts.into_iter().filter_map(|xt| match xt.0.function {569                        RuntimeCall::Ethereum(pallet_ethereum::Call::transact { transaction }) => Some(transaction),570                        _ => None571                    }).collect()572                }573574                fn elasticity() -> Option<Permill> {575                    None576                }577            }578579            impl fp_rpc::ConvertTransactionRuntimeApi<Block> for Runtime {580                fn convert_transaction(transaction: pallet_ethereum::Transaction) -> <Block as BlockT>::Extrinsic  {581                    UncheckedExtrinsic::new_unsigned(582                        pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),583                    )584                }585            }586587            impl sp_session::SessionKeys<Block> for Runtime {588                fn decode_session_keys(589                    encoded: Vec<u8>,590                ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {591                    SessionKeys::decode_into_raw_public_keys(&encoded)592                }593594                fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {595                    SessionKeys::generate(seed)596                }597            }598599            impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {600                fn slot_duration() -> sp_consensus_aura::SlotDuration {601                    sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration())602                }603604                fn authorities() -> Vec<AuraId> {605                    Aura::authorities().to_vec()606                }607            }608609            impl cumulus_primitives_core::CollectCollationInfo<Block> for Runtime {610                fn collect_collation_info(header: &<Block as BlockT>::Header) -> cumulus_primitives_core::CollationInfo {611                    ParachainSystem::collect_collation_info(header)612                }613            }614615            impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {616                fn account_nonce(account: AccountId) -> Index {617                    System::account_nonce(account)618                }619            }620621            impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {622                fn query_info(uxt: <Block as BlockT>::Extrinsic, len: u32) -> RuntimeDispatchInfo<Balance> {623                    TransactionPayment::query_info(uxt, len)624                }625                fn query_fee_details(uxt: <Block as BlockT>::Extrinsic, len: u32) -> FeeDetails<Balance> {626                    TransactionPayment::query_fee_details(uxt, len)627                }628            }629630            /*631            impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber, Hash>632                for Runtime633            {634                fn call(635                    origin: AccountId,636                    dest: AccountId,637                    value: Balance,638                    gas_limit: u64,639                    input_data: Vec<u8>,640                ) -> pallet_contracts_primitives::ContractExecResult {641                    Contracts::bare_call(origin, dest, value, gas_limit, input_data, false)642                }643644                fn instantiate(645                    origin: AccountId,646                    endowment: Balance,647                    gas_limit: u64,648                    code: pallet_contracts_primitives::Code<Hash>,649                    data: Vec<u8>,650                    salt: Vec<u8>,651                ) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, BlockNumber>652                {653                    Contracts::bare_instantiate(origin, endowment, gas_limit, code, data, salt, true, false)654                }655656                fn get_storage(657                    address: AccountId,658                    key: [u8; 32],659                ) -> pallet_contracts_primitives::GetStorageResult {660                    Contracts::get_storage(address, key)661                }662663                fn rent_projection(664                    address: AccountId,665                ) -> pallet_contracts_primitives::RentProjectionResult<BlockNumber> {666                    Contracts::rent_projection(address)667                }668            }669            */670671            #[cfg(feature = "runtime-benchmarks")]672            impl frame_benchmarking::Benchmark<Block> for Runtime {673                fn benchmark_metadata(extra: bool) -> (674                    Vec<frame_benchmarking::BenchmarkList>,675                    Vec<frame_support::traits::StorageInfo>,676                ) {677                    use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList};678                    use frame_support::traits::StorageInfoTrait;679680                    let mut list = Vec::<BenchmarkList>::new();681682                    list_benchmark!(list, extra, pallet_evm_migration, EvmMigration);683                    list_benchmark!(list, extra, pallet_common, Common);684                    list_benchmark!(list, extra, pallet_unique, Unique);685                    list_benchmark!(list, extra, pallet_structure, Structure);686                    list_benchmark!(list, extra, pallet_inflation, Inflation);687688                    #[cfg(feature = "app-promotion")]689                    list_benchmark!(list, extra, pallet_app_promotion, AppPromotion);690691                    list_benchmark!(list, extra, pallet_fungible, Fungible);692                    list_benchmark!(list, extra, pallet_nonfungible, Nonfungible);693694                    #[cfg(feature = "refungible")]695                    list_benchmark!(list, extra, pallet_refungible, Refungible);696697                    #[cfg(feature = "scheduler")]698                    list_benchmark!(list, extra, pallet_unique_scheduler_v2, Scheduler);699700                    #[cfg(feature = "rmrk")]701                    list_benchmark!(list, extra, pallet_proxy_rmrk_core, RmrkCore);702703                    #[cfg(feature = "rmrk")]704                    list_benchmark!(list, extra, pallet_proxy_rmrk_equip, RmrkEquip);705706                    // todo:collator check benchmarks707                    #[cfg(feature = "collator-selection")]708                    list_benchmark!(list, extra, pallet_collator_selection, CollatorSelection);709710                    #[cfg(feature = "foreign-assets")]711                    list_benchmark!(list, extra, pallet_foreign_assets, ForeignAssets);712713714                    // list_benchmark!(list, extra, pallet_evm_coder_substrate, EvmCoderSubstrate);715716                    let storage_info = AllPalletsReversedWithSystemFirst::storage_info();717718                    return (list, storage_info)719                }720721                fn dispatch_benchmark(722                    config: frame_benchmarking::BenchmarkConfig723                ) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {724                    use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};725726                    let allowlist: Vec<TrackedStorageKey> = vec![727                        // Total Issuance728                        hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),729730                        // Block Number731                        hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),732                        // Execution Phase733                        hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),734                        // Event Count735                        hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),736                        // System Events737                        hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),738739                        // Evm CurrentLogs740                        hex_literal::hex!("1da53b775b270400e7e61ed5cbc5a146547f210cec367e9af919603343b9cb56").to_vec().into(),741742                        // Transactional depth743                        hex_literal::hex!("3a7472616e73616374696f6e5f6c6576656c3a").to_vec().into(),744                    ];745746                    let mut batches = Vec::<BenchmarkBatch>::new();747                    let params = (&config, &allowlist);748749                    add_benchmark!(params, batches, pallet_evm_migration, EvmMigration);750                    add_benchmark!(params, batches, pallet_common, Common);751                    add_benchmark!(params, batches, pallet_unique, Unique);752                    add_benchmark!(params, batches, pallet_structure, Structure);753                    add_benchmark!(params, batches, pallet_inflation, Inflation);754755                    #[cfg(feature = "app-promotion")]756                    add_benchmark!(params, batches, pallet_app_promotion, AppPromotion);757758                    add_benchmark!(params, batches, pallet_fungible, Fungible);759                    add_benchmark!(params, batches, pallet_nonfungible, Nonfungible);760761                    #[cfg(feature = "refungible")]762                    add_benchmark!(params, batches, pallet_refungible, Refungible);763764                    #[cfg(feature = "scheduler")]765                    add_benchmark!(params, batches, pallet_unique_scheduler_v2, Scheduler);766767                    #[cfg(feature = "rmrk")]768                    add_benchmark!(params, batches, pallet_proxy_rmrk_core, RmrkCore);769770                    #[cfg(feature = "rmrk")]771                    add_benchmark!(params, batches, pallet_proxy_rmrk_equip, RmrkEquip);772773                    // todo:collator check benchmarks774                    #[cfg(feature = "collator-selection")]775                    add_benchmark!(params, batches, pallet_collator_selection, CollatorSelection);776777                    #[cfg(feature = "foreign-assets")]778                    add_benchmark!(params, batches, pallet_foreign_assets, ForeignAssets);779780                    // add_benchmark!(params, batches, pallet_evm_coder_substrate, EvmCoderSubstrate);781782                    if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }783                    Ok(batches)784                }785            }786787            #[cfg(feature = "try-runtime")]788            impl frame_try_runtime::TryRuntime<Block> for Runtime {789                fn on_runtime_upgrade() -> (frame_support::pallet_prelude::Weight, frame_support::pallet_prelude::Weight) {790                    log::info!("try-runtime::on_runtime_upgrade unique-chain.");791                    let weight = Executive::try_runtime_upgrade().unwrap();792                    (weight, crate::config::substrate::RuntimeBlockWeights::get().max_block)793                }794795                fn execute_block(796                    block: Block,797                    state_root_check: bool,798                    select: frame_try_runtime::TryStateSelect799                ) -> frame_support::pallet_prelude::Weight {800                    log::info!(801                        target: "node-runtime",802                        "try-runtime: executing block {:?} / root checks: {:?} / try-state-select: {:?}",803                        block.header.hash(),804                        state_root_check,805                        select,806                    );807808                    Executive::try_execute_block(block, state_root_check, select).unwrap()809                }810            }811        }812    }813}
modifiedtests/src/collatorSelection.test.tsdiffbeforeafterboth
--- a/tests/src/collatorSelection.test.ts
+++ b/tests/src/collatorSelection.test.ts
@@ -17,90 +17,256 @@
 import {IKeyringPair} from '@polkadot/types/types';
 import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from './util';
 
-// todo Most preferable to launch this test in parallel somehow -- or change the session period (1 hr).
-describe('Integration Test: Dynamic shuffling of collators', () => {
+async function resetInvulnerables() {
+  await usingPlaygrounds(async (helper, privateKey) => {
+    const superuser = await privateKey('//Alice');
+    const alice = await privateKey('//Alice');
+    const bob = await privateKey('//Bob');
+    const invulnerables = await helper.collatorSelection.getInvulnerables();
+    if (!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {
+      console.warn('Alice and Bob are not the invulnerables! Reinstating them back. ' 
+        + 'Current invulnerables\' size: ' + invulnerables.length);
+      
+      let nonce = await helper.chain.getNonce(alice.address);
+      await Promise.all([
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
+      ]);
+
+      nonce = await helper.chain.getNonce(alice.address);
+      await Promise.all(invulnerables.map((invulnerable: any) => {
+        if (invulnerable == alice.address || invulnerable == bob.address) return new Promise<void>(res => res());
+        return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});
+      }));
+    }
+  });
+}
+
+// todo:collator Most preferable to launch this test in parallel somehow -- or change the session period (1 hr).
+// + 18 tests: 5 (1+4) on session change
+describe('Integration Test: Collator Selection', () => {
   let superuser: IKeyringPair;
 
   // These are the default invulnerables, and should return to be invulnerables after this suite.
-  let aliceAddress: string;
-  let bobAddress: string;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
 
   let charlie: IKeyringPair;
   let dave: IKeyringPair;
   //let eve: IKeyringPair;
 
-  before(async function() {
+  before(async function() {  
     await usingPlaygrounds(async (helper, privateKey) => {
       requirePalletsOrSkip(this, helper, [Pallets.CollatorSelection]);
 
+      //todo:collator
       //const donor = await privateKey({filename: __filename});
       //[charlie, dave] = await helper.arrange.createAccounts([100n, 100n], donor);
+      alice = await privateKey('//Alice');
+      bob = await privateKey('//Bob');
       charlie = await privateKey('//Charlie');
       dave = await privateKey('//Dave');
 
       superuser = await privateKey('//Alice');
-      aliceAddress = (await privateKey('//Alice')).address;
-      bobAddress = (await privateKey('//Bob')).address;
+    });
+  });
+
+  describe('Dynamic shuffling of collators', () => {
+    before(async function() {  
+      await usingPlaygrounds(async (helper) => {
+        expect((await helper.collatorSelection.setOwnKeys(charlie))
+          .status.toLowerCase()).to.be.equal('success');
+        expect((await helper.collatorSelection.setOwnKeys(dave))
+          .status.toLowerCase()).to.be.equal('success');
+  
+        // todo:collator check necessity + add RPC for invulnerables / just improve in general
+        // validators = await helper.callRpc('api.query.session.validators');
+        const invulnerables = await helper.callRpc('api.query.collatorSelection.invulnerables');
+        if (!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {
+          console.warn('Alice and Bob are not the invulnerables! Reinstating them back. ' 
+            + 'Current invulnerables\' size: ' + invulnerables.length);
+          
+          await Promise.all([
+            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: 0}),
+            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: 1}),
+          ]);
+  
+          let nonce = 0;
+          await Promise.all(invulnerables.map((invulnerable: any) => {
+            if (invulnerable == alice.address || invulnerable == bob.address) return new Promise((res) => res);
+            return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});
+          }));
+        }
+      });
+    });
+  
+    itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {
+      await expect(Promise.all([
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [charlie.address], true, {nonce: 0}),
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [dave.address], true, {nonce: 1}),
+      ])).to.be.fulfilled;
+  
+      await expect(Promise.all([
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alice.address], true, {nonce: 0}),
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [bob.address], true, {nonce: 1}),
+      ])).to.be.fulfilled;
+  
+      const newInvulnerables = await helper.callRpc('api.query.collatorSelection.invulnerables');
+      expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
+  
+      const expectedSessionIndex = (await helper.callRpc('api.query.session.currentIndex')).toNumber() + 2;
+      let currentSessionIndex = -1;
+      console.log('Waiting for the session after the next.' 
+        + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');
+  
+      while (currentSessionIndex < expectedSessionIndex) {
+        // eslint-disable-next-line no-async-promise-executor
+        currentSessionIndex = await expect(helper.wait.withTimeout(new Promise(async (resolve) => {
+          //todo:collator
+          console.log('starting wait...');
+          console.time('ein');
+          await helper.wait.newBlocks(1);
+          console.timeLog('ein');
+          const res = (await helper.callRpc('api.query.session.currentIndex')).toNumber();
+          console.timeEnd('ein');
+          resolve(res);
+        }), 24000, 'The chain has stopped producing blocks!')).to.be.fulfilled;
+      }
+  
+      const newValidators = await helper.callRpc('api.query.session.validators');
+      expect(newValidators).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
+  
+      const lastBlockNumber = await helper.chain.getLatestBlockNumber();
+      await helper.wait.newBlocks(1);
+      const lastCharlieBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [charlie.address])).toNumber();
+      const lastDaveBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [dave.address])).toNumber();
+      expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true;
+    });
+  
+    // todo:collator keyless invulnerables? will hang, so, a breaking test, eh
+    // register candidate without sudos and the like
+  
+    after(async () => {
+      await usingPlaygrounds(async (helper) => {
+        if (helper.fetchMissingPalletNames([Pallets.CollatorSelection]).length != 0) return;
+
+        await Promise.all([
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: 0}),
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: 1}),
+        ]);
+  
+        await Promise.all([
+          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [charlie.address], true, {nonce: 0}),
+          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [dave.address], true, {nonce: 1}),
+        ]);
+      });
+    });
+  });
+
+  // todo:collator make sure that there is enough session time for a set of tests
+  // 28 non-functioning collators, teehee.
+
+  describe('Addition and removal of invulnerables', () => {
+    before(async function() {
+      await resetInvulnerables();
+    });
 
-      expect((await helper.executeExtrinsic(charlie, 'api.tx.session.setKeys', [
-        '0x' + Buffer.from(charlie.addressRaw).toString('hex'),
-        '0x0',
-      ])).status.toLowerCase()).to.be.equal('success');
+    describe('Positive', () => {
+      itSub('Adds an invulnerable', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([10n], superuser);
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
 
-      expect((await helper.executeExtrinsic(dave, 'api.tx.session.setKeys', [
-        '0x' + Buffer.from(dave.addressRaw).toString('hex'),
-        '0x0',
-      ])).status.toLowerCase()).to.be.equal('success');
+        await helper.collatorSelection.setOwnKeys(account);
+        await helper.getSudo().collatorSelection.addInvulnerable(superuser, account.address);
+        
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(invulnerables.concat(account.address)).to.have.all.members(newInvulnerables);
+      });
 
-      const validators = await helper.callRpc('api.query.session.validators');
-      expect(validators).to.not.contain(charlie.address).and.not.contain(dave.address);
+      itSub('Removes an invulnerable', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        const lastInvulnerable = invulnerables.pop();
+
+        await helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable);
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        // invulnerables had its last element removed, so they should be equal
+        expect(newInvulnerables).to.have.all.members(invulnerables);
+      });
     });
-  });
 
-  itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {
+    describe('Negative', () => {
+      itSub('Does not duplicate an invulnerable', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        // adding an already invulnerable should not fail, but should not duplicate it either
+        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, invulnerables[0]))
+          .to.be.fulfilled;
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(newInvulnerables).to.have.all.members(invulnerables);
+      });
+
+      itSub('Cannot allow invulnerables to be empty', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        const lastInvulnerable = invulnerables.pop();
+
+        let nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(invulnerables.map((i: any) => 
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i], true, {nonce: nonce++})));
 
-    const tx = helper.constructApiCall('api.tx.collatorSelection.setInvulnerables', [[
-      charlie.address,
-      dave.address,
-    ]]);
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [tx])).to.be.fulfilled;
+        await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable))
+          .to.be.rejected;//todo:collator With(/collatorSelection.TooFewInvulnerables/);
+
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(newInvulnerables).to.be.deep.equal([lastInvulnerable]);
+        
+        // restore the invulnerables to the previous state
+        nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(invulnerables.map((i: any) => 
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i], true, {nonce: nonce++})));
+      });
+
+      itSub('Cannot have too many invulnerables', async ({helper}) => {
+        const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;
+        const invulnerablesUntilLimit = 30 - invulnerablesLength;
+        const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);
+        const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);
 
-    const newInvulnerables = await helper.callRpc('api.query.collatorSelection.invulnerables');
-    expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
+        await Promise.all(newInvulnerables.map((i: IKeyringPair) => 
+          helper.collatorSelection.setOwnKeys(i)));
+        await helper.collatorSelection.setOwnKeys(lastInvulnerable);
 
-    const expectedSessionIndex = (await helper.callRpc('api.query.session.currentIndex')).toNumber() + 2;
-    let currentSessionIndex = -1;
-    console.log('Waiting for the session after the next.' 
-      + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');
+        let nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(newInvulnerables.map((i: IKeyringPair) => 
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i.address], true, {nonce: nonce++})));
 
-    while (currentSessionIndex < expectedSessionIndex) {
-      // eslint-disable-next-line no-async-promise-executor
-      currentSessionIndex = await expect(helper.wait.withTimeout(new Promise(async (resolve) => {
-        await helper.wait.newBlocks(1);
-        const res = (await helper.callRpc('api.query.session.currentIndex')).toNumber();
-        resolve(res);
-      }), 24000, 'The chain has stopped producing blocks!')).to.be.fulfilled;
-    }
+        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, lastInvulnerable.address))
+          .to.be.rejected; // todo:collator With(/collatorSelection.TooManyInvulnerables/);
+        
+        // restore the invulnerables to the previous state
+        nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(newInvulnerables.map((i: IKeyringPair) => 
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i.address], true, {nonce: nonce++})));
+      });
 
-    const newValidators = await helper.callRpc('api.query.session.validators');
-    expect(newValidators).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
+      itSub('Forbids a non-sudo to add an invulnerable', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([10n], bob);
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
 
-    const lastBlockNumber = await helper.chain.getLatestBlockNumber();
-    await helper.wait.newBlocks(1);
-    const lastCharlieBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [charlie.address])).toNumber();
-    const lastDaveBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [dave.address])).toNumber();
-    expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true;
-  });
+        await helper.collatorSelection.setOwnKeys(account);
+        await expect(helper.collatorSelection.addInvulnerable(bob, account.address))
+          .to.be.rejectedWith(/BadOrigin/);
 
-  after(async () => {
-    await usingPlaygrounds(async (helper) => {
-      if (helper.fetchMissingPalletNames([Pallets.AppPromotion]).length != 0) return;
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(newInvulnerables).to.be.members(invulnerables);
+      });
 
-      const tx = helper.constructApiCall('api.tx.collatorSelection.setInvulnerables', [[
-        aliceAddress,
-        bobAddress,
-      ]]);
-      await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [tx])).to.be.fulfilled;
+      itSub('Forbids a non-sudo to remove an invulnerable', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        await expect(helper.collatorSelection.removeInvulnerable(superuser, invulnerables[0]))
+          .to.be.rejectedWith(/BadOrigin/);
+        expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);
+      });
     });
+    
+    // todo:collator after
   });
-});
+});
\ No newline at end of file
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -11,6 +11,7 @@
 import {IKeyringPair} from '@polkadot/types/types';
 import {hexToU8a} from '@polkadot/util/hex';
 import {u8aConcat} from '@polkadot/util/u8a';
+import {BN} from '@polkadot/util/bn';
 import {
   IApiListeners,
   IBlock,
@@ -2642,6 +2643,37 @@
   }
 }
 
+class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {
+  //todo:collator documentation
+  setKeys(signer: TSigner, key: string) {
+    return this.helper.executeExtrinsic(
+      signer,
+      'api.tx.session.setKeys', 
+      [
+        key,
+        '0x0',
+      ],
+      true,
+    );
+  }
+
+  setOwnKeys(signer: TSigner) {
+    return this.setKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));
+  }
+
+  addInvulnerable(signer: TSigner, address: string) {
+    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);
+  }
+
+  removeInvulnerable(signer: TSigner, address: string) {
+    return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);
+  }
+
+  async getInvulnerables() {
+    return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());
+  }
+}
+
 class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {
   async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {
     await this.helper.executeExtrinsic(
@@ -2808,6 +2840,7 @@
   ft: FTGroup;
   staking: StakingGroup;
   scheduler: SchedulerGroup;
+  collatorSelection: CollatorSelectionGroup;
   foreignAssets: ForeignAssetsGroup;
   xcm: XcmGroup<UniqueHelper>;
   xTokens: XTokensGroup<UniqueHelper>;
@@ -2823,6 +2856,7 @@
     this.ft = new FTGroup(this);
     this.staking = new StakingGroup(this);
     this.scheduler = new SchedulerGroup(this);
+    this.collatorSelection = new CollatorSelectionGroup(this);
     this.foreignAssets = new ForeignAssetsGroup(this);
     this.xcm = new XcmGroup(this, 'polkadotXcm');
     this.xTokens = new XTokensGroup(this);
@@ -2988,19 +3022,32 @@
       super(...args);
     }
 
-    executeExtrinsic (
+    async executeExtrinsic(
       sender: IKeyringPair,
       extrinsic: string,
       params: any[],
       expectSuccess?: boolean,
+      options: Partial<SignerOptions>|null = null,
     ): Promise<ITransactionResult> {
       const call = this.constructApiCall(extrinsic, params);
-      return super.executeExtrinsic(
+      const result = await super.executeExtrinsic(
         sender,
         'api.tx.sudo.sudo',
         [call],
         expectSuccess,
+        options,
       );
+
+      if (result.status === 'Fail') return result;
+
+      const data = this.eventHelper.extractEvents(result.result.events).find(x => x.section == 'sudo')?.data[0];
+      if (data.err) {
+        const error = data.err.module;
+        // todo:collator
+        const metaError = super.getApi()?.registry.findMetaError({index: new BN(error.index), error: new BN(9)});
+        throw new Error(`${data.err.module.error} ${metaError.section}.${metaError.name}`);
+      }
+      return result;
     }
   };
 }