difftreelog
feat(collator-selection) interaction with configuration pallet
in: master
17 files changed
node/cli/src/chain_spec.rsdiffbeforeafterboth24use serde_json::map::Map;24use serde_json::map::Map;252526use up_common::types::opaque::*;26use up_common::types::opaque::*;27use up_common::constants::{GENESIS_LICENSE_BOND, SESSION_LENGTH};282729#[cfg(feature = "unique-runtime")]28#[cfg(feature = "unique-runtime")]30pub use unique_runtime as default_runtime;29pub use unique_runtime as default_runtime;196 .cloned()195 .cloned()197 .map(|(acc, _)| acc)196 .map(|(acc, _)| acc)198 .collect(),197 .collect(),199 desired_collators: 10,200 license_bond: GENESIS_LICENSE_BOND,201 kick_threshold: SESSION_LENGTH,202 },198 },203 session: SessionConfig {199 session: SessionConfig {204 keys: $initial_invulnerables200 keys: $initial_invulnerablesnode/cli/src/service.rsdiffbeforeafterboth921 .import_notification_stream()921 .import_notification_stream()922 .map(|_| EngineCommand::SealNewBlock {922 .map(|_| EngineCommand::SealNewBlock {923 create_empty: true,923 create_empty: true,924 finalize: false,924 finalize: false, // todo:collator finalize true925 parent_hash: None,925 parent_hash: None,926 sender: None,926 sender: None,927 }),927 }),932 dyn Stream<Item = EngineCommand<Hash>> + Send + Sync + Unpin,932 dyn Stream<Item = EngineCommand<Hash>> + Send + Sync + Unpin,933 > = Box::new(autoseal_interval.map(|_| EngineCommand::SealNewBlock {933 > = Box::new(autoseal_interval.map(|_| EngineCommand::SealNewBlock {934 create_empty: true,934 create_empty: true,935 finalize: false,935 finalize: false, // todo:collator finalize true936 parent_hash: None,936 parent_hash: None,937 sender: None,937 sender: None,938 }));938 }));pallets/collator-selection/Cargo.tomldiffbeforeafterboth25frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" }25frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" }26pallet-authorship = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" }26pallet-authorship = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" }27pallet-session = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" }27pallet-session = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" }28pallet-configuration = { default-features = false, path = "../configuration" }282929frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" }30frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" }3031pallets/collator-selection/src/lib.rsdiffbeforeafterboth105 },105 },106 BoundedVec, PalletId,106 BoundedVec, PalletId,107 };107 };108 use frame_system::{pallet_prelude::*, Config as SystemConfig};108 use frame_system::pallet_prelude::*;109 use pallet_session::SessionManager;109 use pallet_session::SessionManager;110 use sp_runtime::{110 use sp_runtime::{Perbill, traits::Convert};111 Perbill,112 traits::{One, Convert},113 };114 use sp_staking::SessionIndex;111 use pallet_configuration::{115116 type BalanceOf<T> =117 <<T as Config>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;112 CollatorSelectionDesiredCollatorsOverride as DesiredCollators,113 CollatorSelectionLicenseBondOverride as LicenseBond,114 CollatorSelectionKickThresholdOverride as KickThreshold, BalanceOf,115 };116 use sp_staking::SessionIndex;118117119 /// A convertor from collators id. Since this pallet does not have stash/controller, this is118 /// A convertor from collators id. Since this pallet does not have stash/controller, this is120 /// just identity.119 /// just identity.127126128 /// Configure the pallet by specifying the parameters and types on which it depends.127 /// Configure the pallet by specifying the parameters and types on which it depends.129 #[pallet::config]128 #[pallet::config]130 pub trait Config: frame_system::Config {129 pub trait Config: frame_system::Config + pallet_configuration::Config {131 /// Overarching event type.130 /// Overarching event type.132 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;131 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;133134 /// The currency mechanism.135 type Currency: ReservableCurrency<Self::AccountId>;136132137 /// Origin that can dictate updating parameters of this pallet.133 /// Origin that can dictate updating parameters of this pallet.138 type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;134 type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;176172177 /// The (community) collation license holders.173 /// The (community) collation license holders.178 #[pallet::storage]174 #[pallet::storage]179 #[pallet::getter(fn licenses)]175 #[pallet::getter(fn license_deposit_of)]180 pub type Licenses<T: Config> =176 pub type LicenseDepositOf<T: Config> =181 StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf<T>, ValueQuery>;177 StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf<T>, ValueQuery>;182178183 /// The (community, limited) collation candidates.179 /// The (community, limited) collation candidates.186 pub type Candidates<T: Config> =182 pub type Candidates<T: Config> =187 StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;183 StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;188189 /// Collator will be kicked if it does not produce a block within the threshold (does not apply to invulnerables).190 ///191 /// Should be a multiple of session or things will get inconsistent. todo:collator reword?192 #[pallet::storage]193 #[pallet::getter(fn kick_threshold)]194 pub type KickThreshold<T: Config> = StorageValue<_, T::BlockNumber, ValueQuery>;195184196 /// Last block authored by collator.185 /// Last block authored by collator.197 #[pallet::storage]186 #[pallet::storage]198 #[pallet::getter(fn last_authored_block)]187 #[pallet::getter(fn last_authored_block)]199 pub type LastAuthoredBlock<T: Config> =188 pub type LastAuthoredBlock<T: Config> =200 StorageMap<_, Twox64Concat, T::AccountId, T::BlockNumber, ValueQuery>;189 StorageMap<_, Twox64Concat, T::AccountId, T::BlockNumber, ValueQuery>;201202 /// Desired number of candidates.203 ///204 /// This should ideally always be less than [`Config::MaxCollators`] for weights to be correct.205 #[pallet::storage]206 #[pallet::getter(fn desired_collators)]207 pub type DesiredCollators<T> = StorageValue<_, u32, ValueQuery>;208209 /// Fixed amount to deposit to become a collator.210 ///211 /// When a collator calls `leave_intent` they immediately receive the deposit back.212 #[pallet::storage]213 #[pallet::getter(fn license_bond)]214 pub type LicenseBond<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;215190216 #[pallet::genesis_config]191 #[pallet::genesis_config]217 pub struct GenesisConfig<T: Config> {192 pub struct GenesisConfig<T: Config> {218 pub invulnerables: Vec<T::AccountId>,193 pub invulnerables: Vec<T::AccountId>,219 pub license_bond: BalanceOf<T>,220 pub kick_threshold: T::BlockNumber,221 pub desired_collators: u32,222 }194 }223195224 #[cfg(feature = "std")]196 #[cfg(feature = "std")]225 impl<T: Config> Default for GenesisConfig<T> {197 impl<T: Config> Default for GenesisConfig<T> {226 fn default() -> Self {198 fn default() -> Self {227 Self {199 Self {228 invulnerables: Default::default(),200 invulnerables: Default::default(),229 license_bond: Default::default(),230 kick_threshold: T::BlockNumber::one(),231 desired_collators: Default::default(),232 }201 }233 }202 }234 }203 }248 let bounded_invulnerables =217 let bounded_invulnerables =249 BoundedVec::<_, T::MaxCollators>::try_from(self.invulnerables.clone())218 BoundedVec::<_, T::MaxCollators>::try_from(self.invulnerables.clone())250 .expect("genesis invulnerables are more than T::MaxCollators");219 .expect("genesis invulnerables are more than T::MaxCollators");251 assert!(220 252 T::MaxCollators::get() >= self.desired_collators,253 "genesis desired_collators are more than T::MaxCollators",254 );255256 <DesiredCollators<T>>::put(self.desired_collators);257 <LicenseBond<T>>::put(self.license_bond);258 <KickThreshold<T>>::put(self.kick_threshold);259 <Invulnerables<T>>::put(bounded_invulnerables);221 <Invulnerables<T>>::put(bounded_invulnerables);260 }222 }261 }223 }262224263 #[pallet::event]225 #[pallet::event]264 #[pallet::generate_deposit(pub(super) fn deposit_event)]226 #[pallet::generate_deposit(pub(super) fn deposit_event)]265 pub enum Event<T: Config> {227 pub enum Event<T: Config> {266 NewDesiredCollators {267 desired_collators: u32,268 },269 NewLicenseBond {270 bond_amount: BalanceOf<T>,271 },272 NewKickThreshold {273 length_in_blocks: T::BlockNumber,274 },275 InvulnerableAdded {228 InvulnerableAdded {276 invulnerable: T::AccountId,229 invulnerable: T::AccountId,277 },230 },383 Ok(().into())336 Ok(().into())384 }337 }385386 /// Set the ideal number of collators. If lowering this number,387 /// then the number of running collators could be higher than this figure.388 /// Aside from that edge case, there should be no other way to have more collators than the desired number.389 #[pallet::weight(T::WeightInfo::set_desired_collators())]390 pub fn set_desired_collators(origin: OriginFor<T>, max: u32) -> DispatchResultWithPostInfo {391 T::UpdateOrigin::ensure_origin(origin)?;392 // we trust origin calls, this is just a for more accurate benchmarking393 if max > T::MaxCollators::get() {394 log::warn!("max > T::MaxCollators; you might need to run benchmarks again");395 }396 <DesiredCollators<T>>::put(max);397 Self::deposit_event(Event::NewDesiredCollators {398 desired_collators: max,399 });400 Ok(().into())401 }402403 /// Set the candidacy bond amount.404 #[pallet::weight(T::WeightInfo::set_license_bond())]405 pub fn set_license_bond(406 origin: OriginFor<T>,407 bond: BalanceOf<T>,408 ) -> DispatchResultWithPostInfo {409 T::UpdateOrigin::ensure_origin(origin)?;410 <LicenseBond<T>>::put(bond);411 Self::deposit_event(Event::NewLicenseBond { bond_amount: bond });412 Ok(().into())413 }414415 /// Set the length of the kick threshold.416 /// Note that if the length is not a multiple of the session period, it might get inconsistent.417 #[pallet::weight(T::WeightInfo::set_license_bond())] // todo:collator weight418 pub fn set_kick_threshold(419 origin: OriginFor<T>,420 kick_threshold: T::BlockNumber,421 ) -> DispatchResultWithPostInfo {422 T::UpdateOrigin::ensure_origin(origin)?;423 // todo:collator insert something to guarantee consistency?424 <KickThreshold<T>>::put(kick_threshold);425 Self::deposit_event(Event::NewKickThreshold {426 length_in_blocks: kick_threshold,427 });428 Ok(().into())429 }430338431 /// Purchase a license on block collation for this account.339 /// Purchase a license on block collation for this account.432 /// It does not make it a collator candidate, use `onboard` afterward. The account must340 /// It does not make it a collator candidate, use `onboard` afterward. The account must438 // register_as_candidate346 // register_as_candidate439 let who = ensure_signed(origin)?;347 let who = ensure_signed(origin)?;440348441 if Licenses::<T>::contains_key(&who) {349 if LicenseDepositOf::<T>::contains_key(&who) {442 return Err(Error::<T>::AlreadyHoldingLicense.into());350 return Err(Error::<T>::AlreadyHoldingLicense.into());443 }351 }444352449 Error::<T>::ValidatorNotRegistered357 Error::<T>::ValidatorNotRegistered450 );358 );451359452 let deposit = Self::license_bond();360 let deposit = <LicenseBond<T>>::get();453361454 T::Currency::reserve(&who, deposit)?;362 T::Currency::reserve(&who, deposit)?;455 Licenses::<T>::insert(who.clone(), deposit);363 LicenseDepositOf::<T>::insert(who.clone(), deposit);456364457 Self::deposit_event(Event::LicenseObtained {365 Self::deposit_event(Event::LicenseObtained {458 account_id: who,366 account_id: who,471 let who = ensure_signed(origin)?;379 let who = ensure_signed(origin)?;472380473 // ensure the user obtained the license.381 // ensure the user obtained the license.474 ensure!(Licenses::<T>::contains_key(&who), Error::<T>::NoLicense);382 ensure!(383 LicenseDepositOf::<T>::contains_key(&who),384 Error::<T>::NoLicense385 );475 // ensure we are below limit.386 // ensure we are below limit.476 let length = <Candidates<T>>::decode_len().unwrap_or_default()387 let length = <Candidates<T>>::decode_len().unwrap_or_default()477 + <Invulnerables<T>>::decode_len().unwrap_or_default();388 + <Invulnerables<T>>::decode_len().unwrap_or_default();478 ensure!(389 ensure!(479 (length as u32) < Self::desired_collators(),390 (length as u32) < <DesiredCollators<T>>::get(),480 Error::<T>::TooManyCandidates391 Error::<T>::TooManyCandidates481 );392 );482 ensure!(393 ensure!(495 // First authored block is current block plus kick threshold to handle session delay406 // First authored block is current block plus kick threshold to handle session delay496 <LastAuthoredBlock<T>>::insert(407 <LastAuthoredBlock<T>>::insert(497 who.clone(),408 who.clone(),498 frame_system::Pallet::<T>::block_number() + Self::kick_threshold(),409 frame_system::Pallet::<T>::block_number() + <KickThreshold<T>>::get(),499 );410 );500 Ok(candidates.len())411 Ok(candidates.len())501 }412 }594 /// Removes a candidate if they exist and sends them back their deposit, optionally slashed.505 /// Removes a candidate if they exist and sends them back their deposit, optionally slashed.595 fn try_release_license(who: &T::AccountId, should_slash: bool) -> DispatchResult {506 fn try_release_license(who: &T::AccountId, should_slash: bool) -> DispatchResult {596 let mut deposit_returned = BalanceOf::<T>::default();507 let mut deposit_returned = BalanceOf::<T>::default();597 Licenses::<T>::try_mutate_exists(who, |deposit| -> DispatchResult {508 LicenseDepositOf::<T>::try_mutate_exists(who, |deposit| -> DispatchResult {598 if let Some(deposit) = deposit.take() {509 if let Some(deposit) = deposit.take() {599 if should_slash {510 if should_slash {600 let slashed = T::SlashRatio::get() * deposit;511 let slashed = T::SlashRatio::get() * deposit;640 candidates: BoundedVec<T::AccountId, T::MaxCollators>,551 candidates: BoundedVec<T::AccountId, T::MaxCollators>,641 ) -> BoundedVec<T::AccountId, T::MaxCollators> {552 ) -> BoundedVec<T::AccountId, T::MaxCollators> {642 let now = frame_system::Pallet::<T>::block_number();553 let now = frame_system::Pallet::<T>::block_number();643 let kick_threshold = Self::kick_threshold();554 let kick_threshold = <KickThreshold<T>>::get();644 candidates555 candidates645 .into_iter()556 .into_iter()646 .filter_map(|c| {557 .filter_map(|c| {pallets/collator-selection/src/mock.rsdiffbeforeafterboth63 Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},63 Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},64 CollatorSelection: collator_selection::{Pallet, Call, Storage, Event<T>},64 CollatorSelection: collator_selection::{Pallet, Call, Storage, Event<T>},65 Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent},65 Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent},66 Configuration: pallet_configuration::{Pallet, Call, Storage, Event<T>},66 }67 }67);68);6869200 type WeightInfo = ();201 type WeightInfo = ();201}202}203204parameter_types! {205 pub const MaxCollators: u32 = 5;206 pub const LicenseBond: u64 = 10;207 pub const KickThreshold: u64 = 10;208 // the following values do not matter and are meaningless, etc.209 pub const DefaultWeightToFeeCoefficient: u32 = 100_000;210 pub const DefaultMinGasPrice: u64 = 100_000;211 pub const MaxXcmAllowedLocations: u32 = 16;212 pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);213 pub const DayRelayBlocks: u32 = 1;214}215216impl pallet_configuration::Config for Test {217 type RuntimeEvent = RuntimeEvent;218 type Currency = Balances;219 type DefaultCollatorSelectionMaxCollators = MaxCollators;220 type DefaultCollatorSelectionKickThreshold = KickThreshold;221 type DefaultCollatorSelectionLicenseBond = LicenseBond;222 // the following we don't care about223 type DefaultWeightToFeeCoefficient = DefaultWeightToFeeCoefficient;224 type DefaultMinGasPrice = DefaultMinGasPrice;225 type MaxXcmAllowedLocations = MaxXcmAllowedLocations;226 type AppPromotionDailyRate = AppPromotionDailyRate;227 type DayRelayBlocks = DayRelayBlocks;228}202229203ord_parameter_types! {230ord_parameter_types! {204 pub const RootAccount: u64 = 777;231 pub const RootAccount: u64 = 777;205}232}206233207parameter_types! {234parameter_types! {208 pub const PotId: PalletId = PalletId(*b"PotStake");235 pub const PotId: PalletId = PalletId(*b"PotStake");209 pub const MaxCollators: u32 = 20;210 pub const MaxAuthorities: u32 = 100_000;236 pub const MaxAuthorities: u32 = 100_000;211 pub const SlashRatio: Perbill = Perbill::one();237 pub const SlashRatio: Perbill = Perbill::one();212}238}224250225impl Config for Test {251impl Config for Test {226 type RuntimeEvent = RuntimeEvent;252 type RuntimeEvent = RuntimeEvent;227 type Currency = Balances;228 type UpdateOrigin = EnsureSignedBy<RootAccount, u64>;253 type UpdateOrigin = EnsureSignedBy<RootAccount, u64>;229 type PotId = PotId;254 type PotId = PotId;230 type MaxCollators = MaxCollators;255 type MaxCollators = MaxCollators;257 })282 })258 .collect::<Vec<_>>();283 .collect::<Vec<_>>();259 let collator_selection = collator_selection::GenesisConfig::<Test> {284 let collator_selection = collator_selection::GenesisConfig::<Test> {260 desired_collators: 5,261 license_bond: 10,262 kick_threshold: 10,263 invulnerables,285 invulnerables,264 };286 };265 let session = pallet_session::GenesisConfig::<Test> { keys };287 let session = pallet_session::GenesisConfig::<Test> { keys };pallets/collator-selection/src/tests.rsdiffbeforeafterboth36 assert_noop, assert_ok,36 assert_noop, assert_ok,37 traits::{Currency, GenesisBuild, OnInitialize},37 traits::{Currency, GenesisBuild, OnInitialize},38};38};39use frame_system::RawOrigin;39use pallet_balances::Error as BalancesError;40use pallet_balances::Error as BalancesError;40use sp_runtime::traits::BadOrigin;41use sp_runtime::traits::BadOrigin;42use pallet_configuration::{43 CollatorSelectionDesiredCollatorsOverride as DesiredCollators,44 CollatorSelectionKickThresholdOverride as KickThreshold,45 CollatorSelectionLicenseBondOverride as LicenseBond,46};414742fn get_license_and_onboard(account_id: <Test as frame_system::Config>::AccountId) {48fn get_license_and_onboard(account_id: <Test as frame_system::Config>::AccountId) {43 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(49 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(51#[test]57#[test]52fn basic_setup_works() {58fn basic_setup_works() {53 new_test_ext().execute_with(|| {59 new_test_ext().execute_with(|| {54 assert_eq!(CollatorSelection::desired_collators(), 5);60 assert_eq!(<DesiredCollators<Test>>::get(), 5);55 assert_eq!(CollatorSelection::license_bond(), 10);61 assert_eq!(<LicenseBond<Test>>::get(), 10);566257 assert!(CollatorSelection::candidates().is_empty());63 assert!(CollatorSelection::candidates().is_empty());58 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);64 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);122fn set_desired_collators_works() {128fn set_desired_collators_works() {123 new_test_ext().execute_with(|| {129 new_test_ext().execute_with(|| {124 // given130 // given125 assert_eq!(CollatorSelection::desired_collators(), 5);131 assert_eq!(<DesiredCollators<Test>>::get(), 5);126132127 // can set133 // can set128 assert_ok!(CollatorSelection::set_desired_collators(134 assert_ok!(Configuration::set_collator_selection_desired_collators(129 RuntimeOrigin::signed(RootAccount::get()),135 RawOrigin::Root.into(),130 7136 Some(7)131 ));137 ));132 assert_eq!(CollatorSelection::desired_collators(), 7);138 assert_eq!(<DesiredCollators<Test>>::get(), 7);133139134 // rejects bad origin140 // rejects bad origin135 assert_noop!(141 assert_noop!(136 CollatorSelection::set_desired_collators(RuntimeOrigin::signed(1), 8),142 Configuration::set_collator_selection_desired_collators(143 RuntimeOrigin::signed(1),144 Some(8)145 ),137 BadOrigin146 BadOrigin138 );147 );143fn set_license_bond() {152fn set_license_bond() {144 new_test_ext().execute_with(|| {153 new_test_ext().execute_with(|| {145 // given154 // given146 assert_eq!(CollatorSelection::license_bond(), 10);155 assert_eq!(<LicenseBond<Test>>::get(), 10);147156148 // can set157 // can set149 assert_ok!(CollatorSelection::set_license_bond(158 assert_ok!(Configuration::set_collator_selection_license_bond(150 RuntimeOrigin::signed(RootAccount::get()),159 RawOrigin::Root.into(),151 7160 Some(7)152 ));161 ));153 assert_eq!(CollatorSelection::license_bond(), 7);162 assert_eq!(<LicenseBond<Test>>::get(), 7);154163155 // rejects bad origin.164 // rejects bad origin.156 assert_noop!(165 assert_noop!(157 CollatorSelection::set_license_bond(RuntimeOrigin::signed(1), 8),166 Configuration::set_collator_selection_license_bond(RuntimeOrigin::signed(1), Some(8)),158 BadOrigin167 BadOrigin159 );168 );160 });169 });179fn cannot_onboard_candidate_if_too_many() {188fn cannot_onboard_candidate_if_too_many() {180 new_test_ext().execute_with(|| {189 new_test_ext().execute_with(|| {181 // reset desired candidates190 // reset desired candidates182 <crate::DesiredCollators<Test>>::put(0);191 <pallet_configuration::CollatorSelectionDesiredCollatorsOverride<Test>>::put(0);183192184 // can still get a license.193 // can still get a license.185 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));194 assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(4)));191 );200 );192201193 // reset desired candidates to invulnerables + 1202 // reset desired candidates to invulnerables + 1194 <crate::DesiredCollators<Test>>::put(3);203 <pallet_configuration::CollatorSelectionDesiredCollatorsOverride<Test>>::put(3);195 assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(4)));204 assert_ok!(CollatorSelection::onboard(RuntimeOrigin::signed(4)));196205197 // but no more.206 // but no more.236 new_test_ext().execute_with(|| {245 new_test_ext().execute_with(|| {237 // can add 3 as candidate246 // can add 3 as candidate238 get_license_and_onboard(3);247 get_license_and_onboard(3);239 assert_eq!(CollatorSelection::licenses(3), 10);248 assert_eq!(CollatorSelection::license_deposit_of(3), 10);240 assert_eq!(CollatorSelection::candidates(), vec![3]);249 assert_eq!(CollatorSelection::candidates(), vec![3]);241 assert_eq!(CollatorSelection::last_authored_block(3), 10);250 assert_eq!(CollatorSelection::last_authored_block(3), 10);242 assert_eq!(Balances::free_balance(3), 90);251 assert_eq!(Balances::free_balance(3), 90);257fn becoming_candidate_works() {266fn becoming_candidate_works() {258 new_test_ext().execute_with(|| {267 new_test_ext().execute_with(|| {259 // given268 // given260 assert_eq!(CollatorSelection::desired_collators(), 5);269 assert_eq!(<DesiredCollators<Test>>::get(), 5);261 assert_eq!(CollatorSelection::license_bond(), 10);270 assert_eq!(<LicenseBond<Test>>::get(), 10);262 assert_eq!(CollatorSelection::candidates(), Vec::new());271 assert_eq!(CollatorSelection::candidates(), Vec::new());263 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);272 assert_eq!(CollatorSelection::invulnerables(), vec![1, 2]);264273315 ));324 ));316 // should exclude from candidates, but not revoke the license325 // should exclude from candidates, but not revoke the license317 assert_eq!(CollatorSelection::candidates(), vec![]);326 assert_eq!(CollatorSelection::candidates(), vec![]);318 assert_eq!(CollatorSelection::licenses(3), 10);327 assert_eq!(CollatorSelection::license_deposit_of(3), 10);319 assert_eq!(Balances::free_balance(3), 90);328 assert_eq!(Balances::free_balance(3), 90);320 });329 });321}330}503 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);512 assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);504513505 assert_eq!(CollatorSelection::candidates(), vec![4]);514 assert_eq!(CollatorSelection::candidates(), vec![4]);506 assert_eq!(CollatorSelection::kick_threshold(), 10);515 assert_eq!(<KickThreshold<Test>>::get(), 10);507 assert_eq!(CollatorSelection::last_authored_block(4), 20);516 assert_eq!(CollatorSelection::last_authored_block(4), 20);508517509 initialize_to_block(30);518 initialize_to_block(30);524 let invulnerables = vec![1, 1];533 let invulnerables = vec![1, 1];525534526 let collator_selection = collator_selection::GenesisConfig::<Test> {535 let collator_selection = collator_selection::GenesisConfig::<Test> {527 desired_collators: 5,528 license_bond: 10,529 kick_threshold: 10,530 invulnerables,536 invulnerables,531 };537 };532 // collator selection must be initialized before session.538 // collator selection must be initialized before session.pallets/configuration/src/lib.rsdiffbeforeafterboth36mod pallet {36mod pallet {37 use super::*;37 use super::*;38 use frame_support::{38 use frame_support::{39 traits::Get,39 traits::{Get, ReservableCurrency, Currency},40 pallet_prelude::{StorageValue, ValueQuery, DispatchResult, OptionQuery},40 pallet_prelude::{StorageValue, ValueQuery, DispatchResult, IsType, OptionQuery},41 BoundedVec,41 BoundedVec, log,42 };42 };43 use frame_system::{pallet_prelude::OriginFor, ensure_root};43 use frame_system::{pallet_prelude::OriginFor, ensure_root, Config as SystemConfig};44 use xcm::v1::MultiLocation;44 use xcm::v1::MultiLocation;4546 pub type BalanceOf<T> =47 <<T as Config>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;454846 #[pallet::config]49 #[pallet::config]47 pub trait Config: frame_system::Config {50 pub trait Config: frame_system::Config {51 /// Overarching event type.52 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;5354 /// The currency mechanism.55 type Currency: ReservableCurrency<Self::AccountId>;5648 #[pallet::constant]57 #[pallet::constant]49 type DefaultWeightToFeeCoefficient: Get<u32>;58 type DefaultWeightToFeeCoefficient: Get<u32>;58 #[pallet::constant]67 #[pallet::constant]59 type DayRelayBlocks: Get<Self::BlockNumber>;68 type DayRelayBlocks: Get<Self::BlockNumber>;6970 #[pallet::constant]71 type DefaultCollatorSelectionMaxCollators: Get<u32>;72 #[pallet::constant]73 type DefaultCollatorSelectionLicenseBond: Get<BalanceOf<Self>>;74 #[pallet::constant]75 type DefaultCollatorSelectionKickThreshold: Get<Self::BlockNumber>;60 }76 }7778 #[pallet::event]79 #[pallet::generate_deposit(pub(super) fn deposit_event)]80 pub enum Event<T: Config> {81 NewDesiredCollators {82 desired_collators: Option<u32>,83 },84 NewCollatorLicenseBond {85 bond_cost: Option<BalanceOf<T>>,86 },87 NewCollatorKickThreshold {88 length_in_blocks: Option<T::BlockNumber>,89 },90 }619162 #[pallet::error]92 #[pallet::error]63 pub enum Error<T> {93 pub enum Error<T> {85 pub type AppPromomotionConfigurationOverride<T: Config> =115 pub type AppPromomotionConfigurationOverride<T: Config> =86 StorageValue<Value = AppPromotionConfiguration<T::BlockNumber>, QueryKind = ValueQuery>;116 StorageValue<Value = AppPromotionConfiguration<T::BlockNumber>, QueryKind = ValueQuery>;117118 #[pallet::storage]119 pub type CollatorSelectionDesiredCollatorsOverride<T: Config> = StorageValue<120 Value = u32,121 QueryKind = ValueQuery,122 OnEmpty = T::DefaultCollatorSelectionMaxCollators,123 >;124125 #[pallet::storage]126 pub type CollatorSelectionLicenseBondOverride<T: Config> = StorageValue<127 Value = BalanceOf<T>,128 QueryKind = ValueQuery,129 OnEmpty = T::DefaultCollatorSelectionLicenseBond,130 >;131132 #[pallet::storage]133 pub type CollatorSelectionKickThresholdOverride<T: Config> = StorageValue<134 Value = T::BlockNumber,135 QueryKind = ValueQuery,136 OnEmpty = T::DefaultCollatorSelectionKickThreshold,137 >;8713888 #[pallet::call]139 #[pallet::call]89 impl<T: Config> Pallet<T> {140 impl<T: Config> Pallet<T> {145 Ok(())196 Ok(())146 }197 }198199 #[pallet::weight(T::DbWeight::get().writes(1))]200 pub fn set_collator_selection_desired_collators(201 origin: OriginFor<T>,202 max: Option<u32>,203 ) -> DispatchResult {204 ensure_root(origin)?;205 if let Some(max) = max {206 // we trust origin calls, this is just a for more accurate benchmarking207 if max > T::DefaultCollatorSelectionMaxCollators::get() {208 log::warn!("max > T::DefaultCollatorSelectionMaxCollators; you might need to run benchmarks again");209 }210 <CollatorSelectionDesiredCollatorsOverride<T>>::set(max);211 } else {212 <CollatorSelectionDesiredCollatorsOverride<T>>::kill();213 }214 Self::deposit_event(Event::NewDesiredCollators { desired_collators: max });215 Ok(())216 }217218 #[pallet::weight(T::DbWeight::get().writes(1))]219 pub fn set_collator_selection_license_bond(220 origin: OriginFor<T>,221 amount: Option<BalanceOf<T>>,222 ) -> DispatchResult {223 ensure_root(origin)?;224 if let Some(amount) = amount {225 <CollatorSelectionLicenseBondOverride<T>>::set(amount);226 } else {227 <CollatorSelectionLicenseBondOverride<T>>::kill();228 }229 Self::deposit_event(Event::NewCollatorLicenseBond { bond_cost: amount });230 Ok(())231 }232233 #[pallet::weight(T::DbWeight::get().writes(1))]234 pub fn set_collator_selection_kick_threshold(235 origin: OriginFor<T>,236 threshold: Option<T::BlockNumber>,237 ) -> DispatchResult {238 ensure_root(origin)?;239 if let Some(threshold) = threshold {240 <CollatorSelectionKickThresholdOverride<T>>::set(threshold);241 } else {242 <CollatorSelectionKickThresholdOverride<T>>::kill();243 }244 Self::deposit_event(Event::NewCollatorKickThreshold { length_in_blocks: threshold });245 Ok(())246 }147 }247 }148248149 #[pallet::pallet]249 #[pallet::pallet]primitives/common/src/constants.rsdiffbeforeafterboth46pub const EXISTENTIAL_DEPOSIT: u128 = 0;46pub const EXISTENTIAL_DEPOSIT: u128 = 0;47/// Amount of Balance reserved for candidate registration.47/// Amount of Balance reserved for candidate registration.48pub const GENESIS_LICENSE_BOND: u128 = 1_000_000_000_000 * UNIQUE;48pub const GENESIS_LICENSE_BOND: u128 = 1_000_000_000_000 * UNIQUE;49/// Amount of maximum collators for Collator Selection.50pub const MAX_COLLATORS: u32 = 10;49/// How long a periodic session lasts in blocks.51/// How long a periodic session lasts in blocks.50pub const SESSION_LENGTH: BlockNumber = HOURS;52pub const SESSION_LENGTH: BlockNumber = HOURS;5153runtime/common/config/pallets/collator_selection.rsdiffbeforeafterboth17use frame_support::{parameter_types, PalletId};17use frame_support::{parameter_types, PalletId};18use frame_system::EnsureRoot;18use frame_system::EnsureRoot;19use crate::{19use crate::{20 AccountId, BlockNumber, Runtime, RuntimeEvent, Balances, Aura, Session, SessionKeys,20 AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent, Aura, Session, SessionKeys,21 CollatorSelection, config::pallets::TreasuryAccountId,21 CollatorSelection, Treasury,22 config::pallets::{MaxCollators, SessionPeriod, TreasuryAccountId},22};23};23use sp_runtime::Perbill;24use sp_runtime::Perbill;24use up_common::constants::*;25use up_common::constants::{UNIQUE, MILLIUNIQUE};252626parameter_types! {27parameter_types! {27 pub const SessionPeriod: BlockNumber = SESSION_LENGTH;28 pub const SessionOffset: BlockNumber = 0;28 pub const SessionOffset: BlockNumber = 0;29}29}3030555556parameter_types! {56parameter_types! {57 pub const PotId: PalletId = PalletId(*b"PotStake");57 pub const PotId: PalletId = PalletId(*b"PotStake");58 pub const MaxCollators: u32 = 10;59 pub const SlashRatio: Perbill = Perbill::from_percent(100);58 pub const SlashRatio: Perbill = Perbill::from_percent(100);60}59}616062impl pallet_collator_selection::Config for Runtime {61impl pallet_collator_selection::Config for Runtime {63 type RuntimeEvent = RuntimeEvent;62 type RuntimeEvent = RuntimeEvent;64 type Currency = Balances;65 // We allow root only to execute privileged collator selection operations.63 // We allow root only to execute privileged collator selection operations.66 type UpdateOrigin = EnsureRoot<AccountId>;64 type UpdateOrigin = EnsureRoot<AccountId>;67 type TreasuryAccountId = TreasuryAccountId;65 type TreasuryAccountId = TreasuryAccountId;runtime/common/config/pallets/mod.rsdiffbeforeafterboth25 },25 },26 Runtime, RuntimeEvent, RuntimeCall, Balances,26 Runtime, RuntimeEvent, RuntimeCall, Balances,27};27};28use frame_support::traits::{ConstU32, ConstU64};28use frame_support::traits::{ConstU32, ConstU64, ConstU128};29use up_common::{29use up_common::{30 types::{AccountId, Balance, BlockNumber},30 types::{AccountId, Balance, BlockNumber},31 constants::*,31 constants::*,104104105parameter_types! {105parameter_types! {106 pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);106 pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);107 pub const MaxCollators: u32 = MAX_COLLATORS;108 pub const SessionPeriod: BlockNumber = SESSION_LENGTH;107 pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;109 pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;108}110}111109impl pallet_configuration::Config for Runtime {112impl pallet_configuration::Config for Runtime {113 type RuntimeEvent = RuntimeEvent;114 type Currency = Balances;110 type DefaultWeightToFeeCoefficient = ConstU32<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;115 type DefaultWeightToFeeCoefficient = ConstU32<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;111 type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>;116 type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>;117 type DefaultCollatorSelectionMaxCollators = MaxCollators;118 type DefaultCollatorSelectionKickThreshold = SessionPeriod;119 type DefaultCollatorSelectionLicenseBond =120 ConstU128<{ up_common::constants::GENESIS_LICENSE_BOND }>;112 type MaxXcmAllowedLocations = ConstU32<16>;121 type MaxXcmAllowedLocations = ConstU32<16>;113 type AppPromotionDailyRate = AppPromotionDailyRate;122 type AppPromotionDailyRate = AppPromotionDailyRate;114 type DayRelayBlocks = DayRelayBlocks;123 type DayRelayBlocks = DayRelayBlocks;runtime/common/construct_runtime/mod.rsdiffbeforeafterboth69 // #[runtimes(opal)]69 // #[runtimes(opal)]70 // Scheduler: pallet_unique_scheduler_v2::{Pallet, Call, Storage, Event<T>} = 62,70 // Scheduler: pallet_unique_scheduler_v2::{Pallet, Call, Storage, Event<T>} = 62,717172 Configuration: pallet_configuration::{Pallet, Call, Storage} = 63,72 Configuration: pallet_configuration::{Pallet, Call, Storage, Event<T>} = 63,737374 Charging: pallet_charge_transaction::{Pallet, Call, Storage } = 64,74 Charging: pallet_charge_transaction::{Pallet, Call, Storage } = 64,75 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,75 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,runtime/common/maintenance.rsdiffbeforeafterboth85 Err(TransactionValidityError::Invalid(InvalidTransaction::Call))85 Err(TransactionValidityError::Invalid(InvalidTransaction::Call))86 }86 }8788 #[cfg(feature = "collator-selection")]89 RuntimeCall::CollatorSelection(_)90 | RuntimeCall::Authorship(_)91 | RuntimeCall::Session(_)92 | RuntimeCall::Identity(_) => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),879388 #[cfg(feature = "pallet-test-utils")]94 #[cfg(feature = "pallet-test-utils")]89 RuntimeCall::TestUtils(_) => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),95 RuntimeCall::TestUtils(_) => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),110 ) -> TransactionValidity {116 ) -> TransactionValidity {111 if Maintenance::is_enabled() {117 if Maintenance::is_enabled() {112 match call {118 match call {113 RuntimeCall::EVM(_) | RuntimeCall::Ethereum(_) | RuntimeCall::EvmMigration(_) => {119 RuntimeCall::EVM(_) | RuntimeCall::Ethereum(_) | RuntimeCall::DataManagement(_) => {114 Err(TransactionValidityError::Invalid(InvalidTransaction::Call))120 Err(TransactionValidityError::Invalid(InvalidTransaction::Call))115 }121 }116 _ => Ok(ValidTransaction::default()),122 _ => Ok(ValidTransaction::default()),runtime/common/mod.rsdiffbeforeafterboth187 {187 {188 use frame_support::{BoundedVec, storage::migration};188 use frame_support::{BoundedVec, storage::migration};189 use sp_runtime::{189 use sp_runtime::{traits::OpaqueKeys, RuntimeAppPublic};190 traits::{OpaqueKeys, Saturating},191 RuntimeAppPublic,192 };193 use pallet_session::SessionManager;190 use pallet_session::SessionManager;194 use up_common::constants::{GENESIS_LICENSE_BOND, SESSION_LENGTH};195 use crate::config::pallets::collator_selection::MaxCollators;191 use crate::config::pallets::MaxCollators;196192197 let mut weight = <Runtime as frame_system::Config>::DbWeight::get().reads(1);193 let mut weight = <Runtime as frame_system::Config>::DbWeight::get().reads(1);198194241 .expect("Existing collators/invulnerables are more than MaxCollators");237 .expect("Existing collators/invulnerables are more than MaxCollators");242238243 <pallet_collator_selection::Invulnerables<Runtime>>::put(bounded_invulnerables);239 <pallet_collator_selection::Invulnerables<Runtime>>::put(bounded_invulnerables);244 <pallet_collator_selection::KickThreshold<Runtime>>::put(SESSION_LENGTH);245 <pallet_collator_selection::DesiredCollators<Runtime>>::put(MaxCollators::get());246 <pallet_collator_selection::LicenseBond<Runtime>>::put(GENESIS_LICENSE_BOND);247240248 let keys = invulnerables241 let keys = invulnerables249 .into_iter()242 .into_iter()runtime/common/tests/mod.rsdiffbeforeafterboth646465 let cfg = GenesisConfig {65 let cfg = GenesisConfig {66 collator_selection: CollatorSelectionConfig {66 collator_selection: CollatorSelectionConfig {67 desired_collators: 2,68 license_bond: 10,69 kick_threshold: 10,70 invulnerables,67 invulnerables,71 },68 },72 session: SessionConfig { keys },69 session: SessionConfig { keys },tests/src/collatorSelection.seqtest.tsdiffbeforeafterboth17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';18import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from './util';18import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from './util';1920const MAX_INVULNERABLES = 10;211922async function resetInvulnerables() {20async function resetInvulnerables() {23 await usingPlaygrounds(async (helper, privateKey) => {21 await usingPlaygrounds(async (helper, privateKey) => {31 29 32 let nonce = await helper.chain.getNonce(alice.address);30 let nonce = await helper.chain.getNonce(alice.address);33 // In case there are too many invulnerables already, remove some of them, leaving space for Alice and Bob.31 // In case there are too many invulnerables already, remove some of them, leaving space for Alice and Bob.34 if (invulnerables.length + 2 >= MAX_INVULNERABLES) {32 if (invulnerables.length + 2 >= helper.collatorSelection.maxCollators()) {35 await Promise.all([33 await Promise.all([36 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()], true, {nonce: nonce++}),34 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()], true, {nonce: nonce++}),37 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()], true, {nonce: nonce++}),35 helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()], true, {nonce: nonce++}),409 // 28 non-functioning collators, teehee.407 // 28 non-functioning collators, teehee.410 408 411 const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;409 const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;412 const invulnerablesUntilLimit = MAX_INVULNERABLES - invulnerablesLength;410 const invulnerablesUntilLimit = helper.collatorSelection.maxCollators() - invulnerablesLength;413 const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);411 const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);414 const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);412 const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);415413tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth65 arrange: ArrangeGroup;65 arrange: ArrangeGroup;66 wait: WaitGroup;66 wait: WaitGroup;67 admin: AdminGroup;67 admin: AdminGroup;68 session: SessionGroup;68 testUtils: TestUtilGroup;69 testUtils: TestUtilGroup;697070 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {71 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {75 this.wait = new WaitGroup(this);76 this.wait = new WaitGroup(this);76 this.admin = new AdminGroup(this);77 this.admin = new AdminGroup(this);77 this.testUtils = new TestUtilGroup(this);78 this.testUtils = new TestUtilGroup(this);79 this.session = new SessionGroup(this);78 }80 }798180 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {82 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {456 console.log(`Waiting for ${sessionCount} new session${sessionCount > 1 ? 's' : ''}.` 458 console.log(`Waiting for ${sessionCount} new session${sessionCount > 1 ? 's' : ''}.` 457 + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');459 + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');458460459 const expectedSessionIndex = await this.helper.session.getIndex() + sessionCount;461 const expectedSessionIndex = await (this.helper as DevUniqueHelper).session.getIndex() + sessionCount;460 let currentSessionIndex = -1;462 let currentSessionIndex = -1;461463462 while (currentSessionIndex < expectedSessionIndex) {464 while (currentSessionIndex < expectedSessionIndex) {463 // eslint-disable-next-line no-async-promise-executor465 // eslint-disable-next-line no-async-promise-executor464 currentSessionIndex = await this.withTimeout(new Promise(async (resolve) => {466 currentSessionIndex = await this.withTimeout(new Promise(async (resolve) => {465 await this.newBlocks(1);467 await this.newBlocks(1);466 const res = this.helper.session.getIndex();468 const res = await (this.helper as DevUniqueHelper).session.getIndex();467 resolve(res);469 resolve(res);468 }), blockTimeout, 'The chain has stopped producing blocks!');470 }), blockTimeout, 'The chain has stopped producing blocks!');469 }471 }552 }554 }553}555}556557class SessionGroup {558 helper: ChainHelperBase;559560 constructor(helper: ChainHelperBase) {561 this.helper = helper;562 }563 564 //todo:collator documentation565 async getIndex(): Promise<number> {566 return (await this.helper.callRpc('api.query.session.currentIndex')).toNumber();567 }568569 newSessions(sessionCount = 1, blockTimeout = 24000): Promise<void> {570 return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout);571 }572573 setOwnKeys(signer: TSigner, key: string) {574 return this.helper.executeExtrinsic(575 signer,576 'api.tx.session.setKeys', 577 [key, '0x0'],578 true,579 );580 }581582 setOwnKeysFromAddress(signer: TSigner) {583 return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));584 }585}554586555class TestUtilGroup {587class TestUtilGroup {556 helper: DevUniqueHelper;588 helper: DevUniqueHelper;tests/src/util/playgrounds/unique.tsdiffbeforeafterboth45import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';45import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';46import type {Vec} from '@polkadot/types-codec';46import type {Vec} from '@polkadot/types-codec';47import {FrameSystemEventRecord} from '@polkadot/types/lookup';47import {FrameSystemEventRecord} from '@polkadot/types/lookup';48import {DevUniqueHelper} from './unique.dev';494850export class CrossAccountId implements ICrossAccountId {49export class CrossAccountId implements ICrossAccountId {51 Substrate?: TSubstrateAccount;50 Substrate?: TSubstrateAccount;376 children: ChainHelperBase[];375 children: ChainHelperBase[];377 address: AddressGroup;376 address: AddressGroup;378 chain: ChainGroup;377 chain: ChainGroup;379 session: SessionGroup;380378381 constructor(logger?: ILogger, helperBase?: any) {379 constructor(logger?: ILogger, helperBase?: any) {382 this.helperBase = helperBase;380 this.helperBase = helperBase;392 this.children = [];390 this.children = [];393 this.address = new AddressGroup(this);391 this.address = new AddressGroup(this);394 this.chain = new ChainGroup(this);392 this.chain = new ChainGroup(this);395 this.session = new SessionGroup(this);396 }393 }397394398 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {395 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {2645 }2642 }2646}2643}26472648class SessionGroup extends HelperGroup<ChainHelperBase> {2649 //todo:collator documentation2650 async getIndex(): Promise<number> {2651 return (await this.helper.callRpc('api.query.session.currentIndex')).toNumber();2652 }26532654 newSessions(sessionCount = 1, blockTimeout = 24000): Promise<void> {2655 return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout);2656 }26572658 setOwnKeys(signer: TSigner, key: string) {2659 return this.helper.executeExtrinsic(2660 signer,2661 'api.tx.session.setKeys', 2662 [key, '0x0'],2663 true,2664 );2665 }26662667 setOwnKeysFromAddress(signer: TSigner) {2668 return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));2669 }2670}267126442672class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {2645class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {2673 //todo:collator documentation2646 //todo:collator documentation2683 return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());2656 return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());2684 }2657 }26582659 /** and also total max invulnerables */2660 maxCollators(): number {2661 return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);2662 }26632664 async getDesiredCollators(): Promise<number> {2665 return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();2666 }268526672686 setLicenseBond(signer: TSigner, amount: bigint) {2668 setLicenseBond(signer: TSigner, amount: bigint) {2687 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.setLicenseBond', [amount]);2669 return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]);2688 }2670 }268926712690 async getLicenseBond(): Promise<bigint> {2672 async getLicenseBond(): Promise<bigint> {2691 return (await this.helper.callRpc('api.query.collatorSelection.licenseBond')).toBigInt();2673 return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt();2692 }2674 }269326752694 obtainLicense(signer: TSigner) {2676 obtainLicense(signer: TSigner) {2704 }2686 }270526872706 async hasLicense(address: string): Promise<bigint> {2688 async hasLicense(address: string): Promise<bigint> {2707 return (await this.helper.callRpc('api.query.collatorSelection.licenses', [address])).toBigInt();2689 return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt();2708 }2690 }270926912710 onboard(signer: TSigner) {2692 onboard(signer: TSigner) {