git.delta.rocks / unique-network / refs/commits / 896d7c692dac

difftreelog

feat(collator-selection) interaction with configuration pallet

Fahrrader2022-12-27parent: #70abe57.patch.diff
in: master

17 files changed

modifiednode/cli/src/chain_spec.rsdiffbeforeafterboth
24use serde_json::map::Map;24use serde_json::map::Map;
2525
26use up_common::types::opaque::*;26use up_common::types::opaque::*;
27use up_common::constants::{GENESIS_LICENSE_BOND, SESSION_LENGTH};
2827
29#[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_invulnerables
modifiednode/cli/src/service.rsdiffbeforeafterboth
921 .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 true
925 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 true
936 parent_hash: None,936 parent_hash: None,
937 sender: None,937 sender: None,
938 }));938 }));
modifiedpallets/collator-selection/Cargo.tomldiffbeforeafterboth
25frame-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" }
2829
29frame-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" }
3031
modifiedpallets/collator-selection/src/lib.rsdiffbeforeafterboth
105 },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::{
115
116 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;
118117
119 /// 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 is
120 /// just identity.119 /// just identity.
127126
128 /// 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>;
133
134 /// The currency mechanism.
135 type Currency: ReservableCurrency<Self::AccountId>;
136132
137 /// 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>;
176172
177 /// 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>;
182178
183 /// 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>;
188
189 /// 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>;
195184
196 /// 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>;
201
202 /// 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>;
208
209 /// 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>;
215190
216 #[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 }
223195
224 #[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 );
255
256 <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 }
262224
263 #[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 }
385
386 /// 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 benchmarking
393 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 }
402
403 /// 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 }
414
415 /// 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 weight
418 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 }
430338
431 /// 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 must
438 // register_as_candidate346 // register_as_candidate
439 let who = ensure_signed(origin)?;347 let who = ensure_signed(origin)?;
440348
441 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 }
444352
449 Error::<T>::ValidatorNotRegistered357 Error::<T>::ValidatorNotRegistered
450 );358 );
451359
452 let deposit = Self::license_bond();360 let deposit = <LicenseBond<T>>::get();
453361
454 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);
456364
457 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)?;
472380
473 // 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>::NoLicense
385 );
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>::TooManyCandidates
481 );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 delay
496 <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 candidates
645 .into_iter()556 .into_iter()
646 .filter_map(|c| {557 .filter_map(|c| {
modifiedpallets/collator-selection/src/mock.rsdiffbeforeafterboth
63 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);
6869
200 type WeightInfo = ();201 type WeightInfo = ();
201}202}
203
204parameter_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}
215
216impl 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 about
223 type DefaultWeightToFeeCoefficient = DefaultWeightToFeeCoefficient;
224 type DefaultMinGasPrice = DefaultMinGasPrice;
225 type MaxXcmAllowedLocations = MaxXcmAllowedLocations;
226 type AppPromotionDailyRate = AppPromotionDailyRate;
227 type DayRelayBlocks = DayRelayBlocks;
228}
202229
203ord_parameter_types! {230ord_parameter_types! {
204 pub const RootAccount: u64 = 777;231 pub const RootAccount: u64 = 777;
205}232}
206233
207parameter_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}
224250
225impl 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 };
modifiedpallets/collator-selection/src/tests.rsdiffbeforeafterboth
36 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};
4147
42fn 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);
5662
57 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 // given
125 assert_eq!(CollatorSelection::desired_collators(), 5);131 assert_eq!(<DesiredCollators<Test>>::get(), 5);
126132
127 // can set133 // can set
128 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);
133139
134 // rejects bad origin140 // rejects bad origin
135 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 BadOrigin
138 );147 );
143fn set_license_bond() {152fn set_license_bond() {
144 new_test_ext().execute_with(|| {153 new_test_ext().execute_with(|| {
145 // given154 // given
146 assert_eq!(CollatorSelection::license_bond(), 10);155 assert_eq!(<LicenseBond<Test>>::get(), 10);
147156
148 // can set157 // can set
149 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);
154163
155 // 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 BadOrigin
159 );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 candidates
182 <crate::DesiredCollators<Test>>::put(0);191 <pallet_configuration::CollatorSelectionDesiredCollatorsOverride<Test>>::put(0);
183192
184 // 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 );
192201
193 // reset desired candidates to invulnerables + 1202 // reset desired candidates to invulnerables + 1
194 <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)));
196205
197 // 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 candidate
238 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 // given
260 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]);
264273
315 ));324 ));
316 // should exclude from candidates, but not revoke the license325 // should exclude from candidates, but not revoke the license
317 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]);
504513
505 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);
508517
509 initialize_to_block(30);518 initialize_to_block(30);
524 let invulnerables = vec![1, 1];533 let invulnerables = vec![1, 1];
525534
526 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.
modifiedpallets/configuration/src/lib.rsdiffbeforeafterboth
36mod 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;
45
46 pub type BalanceOf<T> =
47 <<T as Config>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;
4548
46 #[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>;
53
54 /// The currency mechanism.
55 type Currency: ReservableCurrency<Self::AccountId>;
56
48 #[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>;
69
70 #[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 }
77
78 #[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 }
6191
62 #[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>;
117
118 #[pallet::storage]
119 pub type CollatorSelectionDesiredCollatorsOverride<T: Config> = StorageValue<
120 Value = u32,
121 QueryKind = ValueQuery,
122 OnEmpty = T::DefaultCollatorSelectionMaxCollators,
123 >;
124
125 #[pallet::storage]
126 pub type CollatorSelectionLicenseBondOverride<T: Config> = StorageValue<
127 Value = BalanceOf<T>,
128 QueryKind = ValueQuery,
129 OnEmpty = T::DefaultCollatorSelectionLicenseBond,
130 >;
131
132 #[pallet::storage]
133 pub type CollatorSelectionKickThresholdOverride<T: Config> = StorageValue<
134 Value = T::BlockNumber,
135 QueryKind = ValueQuery,
136 OnEmpty = T::DefaultCollatorSelectionKickThreshold,
137 >;
87138
88 #[pallet::call]139 #[pallet::call]
89 impl<T: Config> Pallet<T> {140 impl<T: Config> Pallet<T> {
145 Ok(())196 Ok(())
146 }197 }
198
199 #[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 benchmarking
207 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 }
217
218 #[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 }
232
233 #[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 }
148248
149 #[pallet::pallet]249 #[pallet::pallet]
modifiedprimitives/common/src/constants.rsdiffbeforeafterboth
46pub 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;
5153
modifiedruntime/common/config/pallets/collator_selection.rsdiffbeforeafterboth
17use 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};
2526
26parameter_types! {27parameter_types! {
27 pub const SessionPeriod: BlockNumber = SESSION_LENGTH;
28 pub const SessionOffset: BlockNumber = 0;28 pub const SessionOffset: BlockNumber = 0;
29}29}
3030
5555
56parameter_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}
6160
62impl 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;
modifiedruntime/common/config/pallets/mod.rsdiffbeforeafterboth
25 },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::*,
104104
105parameter_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}
111
109impl 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;
modifiedruntime/common/construct_runtime/mod.rsdiffbeforeafterboth
69 // #[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,
7171
72 Configuration: pallet_configuration::{Pallet, Call, Storage} = 63,72 Configuration: pallet_configuration::{Pallet, Call, Storage, Event<T>} = 63,
7373
74 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,
modifiedruntime/common/maintenance.rsdiffbeforeafterboth
85 Err(TransactionValidityError::Invalid(InvalidTransaction::Call))85 Err(TransactionValidityError::Invalid(InvalidTransaction::Call))
86 }86 }
87
88 #[cfg(feature = "collator-selection")]
89 RuntimeCall::CollatorSelection(_)
90 | RuntimeCall::Authorship(_)
91 | RuntimeCall::Session(_)
92 | RuntimeCall::Identity(_) => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
8793
88 #[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()),
modifiedruntime/common/mod.rsdiffbeforeafterboth
187 {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;
196192
197 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);
198194
241 .expect("Existing collators/invulnerables are more than MaxCollators");237 .expect("Existing collators/invulnerables are more than MaxCollators");
242238
243 <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);
247240
248 let keys = invulnerables241 let keys = invulnerables
249 .into_iter()242 .into_iter()
modifiedruntime/common/tests/mod.rsdiffbeforeafterboth
6464
65 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 },
modifiedtests/src/collatorSelection.seqtest.tsdiffbeforeafterboth
17import {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';
19
20const MAX_INVULNERABLES = 10;
2119
22async 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);
415413
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
65 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;
6970
70 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 }
7981
80 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.');
458460
459 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;
461463
462 while (currentSessionIndex < expectedSessionIndex) {464 while (currentSessionIndex < expectedSessionIndex) {
463 // eslint-disable-next-line no-async-promise-executor465 // eslint-disable-next-line no-async-promise-executor
464 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}
556
557class SessionGroup {
558 helper: ChainHelperBase;
559
560 constructor(helper: ChainHelperBase) {
561 this.helper = helper;
562 }
563
564 //todo:collator documentation
565 async getIndex(): Promise<number> {
566 return (await this.helper.callRpc('api.query.session.currentIndex')).toNumber();
567 }
568
569 newSessions(sessionCount = 1, blockTimeout = 24000): Promise<void> {
570 return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout);
571 }
572
573 setOwnKeys(signer: TSigner, key: string) {
574 return this.helper.executeExtrinsic(
575 signer,
576 'api.tx.session.setKeys',
577 [key, '0x0'],
578 true,
579 );
580 }
581
582 setOwnKeysFromAddress(signer: TSigner) {
583 return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));
584 }
585}
554586
555class TestUtilGroup {587class TestUtilGroup {
556 helper: DevUniqueHelper;588 helper: DevUniqueHelper;
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
45import {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';
4948
50export 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;
380378
381 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 }
397394
398 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {395 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {
2645 }2642 }
2646}2643}
2647
2648class SessionGroup extends HelperGroup<ChainHelperBase> {
2649 //todo:collator documentation
2650 async getIndex(): Promise<number> {
2651 return (await this.helper.callRpc('api.query.session.currentIndex')).toNumber();
2652 }
2653
2654 newSessions(sessionCount = 1, blockTimeout = 24000): Promise<void> {
2655 return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout);
2656 }
2657
2658 setOwnKeys(signer: TSigner, key: string) {
2659 return this.helper.executeExtrinsic(
2660 signer,
2661 'api.tx.session.setKeys',
2662 [key, '0x0'],
2663 true,
2664 );
2665 }
2666
2667 setOwnKeysFromAddress(signer: TSigner) {
2668 return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));
2669 }
2670}
26712644
2672class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {2645class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {
2673 //todo:collator documentation2646 //todo:collator documentation
2683 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 }
2658
2659 /** and also total max invulnerables */
2660 maxCollators(): number {
2661 return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);
2662 }
2663
2664 async getDesiredCollators(): Promise<number> {
2665 return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();
2666 }
26852667
2686 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 }
26892671
2690 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 }
26932675
2694 obtainLicense(signer: TSigner) {2676 obtainLicense(signer: TSigner) {
2704 }2686 }
27052687
2706 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 }
27092691
2710 onboard(signer: TSigner) {2692 onboard(signer: TSigner) {