referrerpolicy=no-referrer-when-downgrade

pallet_staking_async_parachain_runtime/
staking.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18///! Staking, and election related pallet configurations.
19use super::*;
20use cumulus_primitives_core::relay_chain::SessionIndex;
21use frame_election_provider_support::{ElectionDataProvider, SequentialPhragmen};
22use frame_support::traits::{ConstU128, EitherOf};
23use pallet_election_provider_multi_block::{self as multi_block, SolutionAccuracyOf};
24use pallet_staking_async::UseValidatorsMap;
25use pallet_staking_async_rc_client as rc_client;
26use polkadot_runtime_common::{prod_or_fast, BalanceToU256, U256ToBalance};
27use sp_core::Get;
28use sp_npos_elections::BalancingConfig;
29use sp_runtime::{
30	traits::Convert, transaction_validity::TransactionPriority, FixedPointNumber, FixedU128,
31	SaturatedConversion,
32};
33use xcm::latest::prelude::*;
34
35pub(crate) fn enable_dot_preset(fast: bool) {
36	Pages::set(&32);
37	MinerPages::set(&4);
38	MaxElectingVoters::set(&22_500);
39	TargetSnapshotPerBlock::set(&2000);
40	if !fast {
41		SignedValidationPhase::set(&(8 * Pages::get()));
42		SignedPhase::set(&(20 * MINUTES));
43	}
44}
45
46pub(crate) fn enable_ksm_preset(fast: bool) {
47	Pages::set(&16);
48	MinerPages::set(&4);
49	MaxElectingVoters::set(&12_500);
50	TargetSnapshotPerBlock::set(&2500);
51	if !fast {
52		SignedValidationPhase::set(&(4 * Pages::get()));
53		SignedPhase::set(&(20 * MINUTES));
54	}
55}
56
57// This macro contains all of the variable parameters that we intend to use for Polkadot and
58// Kusama.
59//
60// Note that this runtime has 3 broad presets:
61//
62// 1. dev: fast development preset.
63// 2. dot-size: as close to Polkadot as possible.
64// 3. ksm-size: as close to Kusama as possible.
65//
66// The default values here are related to `dev`. The above helper functions are used at launch (see
67// `build_state` runtime-api) to enable dot/ksm presets.
68parameter_types! {
69	/// Number of election pages that we operate upon.
70	///
71	/// * Polkadot: 32 (3.2m snapshot)
72	/// * Kusama: 16 (1.6m snapshot)
73	///
74	/// Reasoning: Both leads to around 700 nominators per-page, yielding the weights in
75	/// https://github.com/paritytech/polkadot-sdk/pull/8704, the maximum of which being around 1mb
76	/// compressed PoV and 2mb uncompressed.
77	///
78	/// NOTE: in principle, there is nothing preventing us from stretching these values further, it
79	/// will only reduce the per-page POVs. Although, some operations like the first snapshot, and
80	/// the last page of export (where we operate on `MaxValidatorSet` validators) will not get any
81	/// better.
82	pub storage Pages: u32 = 4;
83
84	/// * Polkadot: 16 * 32 (512 blocks, 51.2m).
85	/// * Kusama: 8 * 16 (12 blocks, 12.8m).
86	///
87	/// (MaxSubmissions * Pages) for both, enough to verify all solutions.
88	///
89	/// Reasoning: Less security needed in Kusama, to compensate for the shorter session duration.
90	pub storage SignedValidationPhase: u32 = Pages::get() * 2;
91
92	/// * Polkadot: 200 blocks, 20m.
93	/// * Kusama: 100 blocks, 10m.
94	///
95	/// Reasoning:
96	///
97	/// * Polkadot wishes at least 8 submitters to be able to submit. That is  8 * 32 = 256 pages
98	///   for all submitters. Weight of each submission page is roughly 0.0007 of block weight. 200
99	///   blocks is more than enough.
100	/// * Kusama wishes at least 4 submitters to be able to submit. That is 4 * 16 = 64 pages for
101	///   all submitters. Weight of each submission page is roughly 0.0007 of block weight. 100
102	///   blocks is more than enough.
103	///
104	/// See `signed_weight_ratios` test below for more info.
105	pub storage SignedPhase: u32 = 4 * MINUTES;
106
107	/// * Polkadot: 4
108	/// * Kusama: 4
109	///
110	/// Reasoning: with 4 pages, the `ElectionScore` computed in both Kusama and Polkadot is pretty
111	/// good. See and run `run_election_with_pages` below to see. With 4 pages, roughly 2800
112	/// nominators will be elected. This is not great for staking reward, but is good enough for
113	/// chain's economic security.
114	pub storage MinerPages: u32 = 4;
115
116	/// * Polkadot: 300 blocks, 30m
117	/// * Kusama: 150 blocks, 15m
118	///
119	/// Reasoning: The only criteria is for the phase to be long enough such that the OCW miner is
120	/// able to run the mining code at least twice. Note that `OffchainRepeat` limits execution of
121	/// the OCW to at most 4 times per round, for faster collators.
122	///
123	/// Benchmarks logs from tests below are:
124	///
125	/// * exec_time of polkadot miner in WASM with 4 pages is 27369ms
126	/// * exec_time of kusama miner in WASM with 4 pages is 23848ms
127	///
128	/// See `max_ocw_miner_pages_as_per_weights` test below.
129	pub storage UnsignedPhase: u32 = MINUTES;
130
131	/// * Polkadot: 22_500
132	/// * Kusama: 12_500
133	///
134	/// Reasoning: Yielding  703 nominators per page in both. See [`Pages`] for more info. Path to
135	/// Upgrade: We may wish to increase the number of "active nominators" in both networks by 1)
136	/// increasing the `Pages` and `MaxElectingVoters` in sync. This update needs to happen while an
137	/// election is NOT ongoing.
138	pub storage MaxElectingVoters: u32 = 1000;
139
140	/// * Polkadot: 2000 (always equal to `staking.maxValidatorCount`)
141	/// * Kusama: 4000 (always equal to `staking.maxValidatorCount`)
142	///
143	/// Reasoning: As of now, we don't have a way to sort validators, so we wish to select all of
144	/// them. In case this limit is reached, governance should introduce `minValidatorBond`, and
145	/// validators would have to compete with their self-stake to force-chill one another. More
146	/// info: SRL-417
147	pub storage TargetSnapshotPerBlock: u32 = 4000;
148
149	// NOTE: rest of the parameters are computed identically in both Kusama and Polkadot.
150
151	/// Allow OCW miner to at most run 4 times in the entirety of the 10m Unsigned Phase.
152	pub OffchainRepeat: u32 = UnsignedPhase::get() / 4;
153
154	/// Upper bound of `Staking.ValidatorCount`, which translates to
155	/// `ElectionProvider::DesiredTargets`. 1000 is the end-game for both Kusama and Polkadot for
156	/// the foreseeable future.
157	pub const MaxValidatorSet: u32 = 1000;
158
159	/// Number of nominators per page of the snapshot, and consequently number of backers in the
160	/// solution.
161	///
162	/// 703 in both Polkadot and Kusama.
163	pub VoterSnapshotPerBlock: u32 = MaxElectingVoters::get() / Pages::get();
164
165	/// In each page, we may observe up to all of the validators.
166	pub const MaxWinnersPerPage: u32 = MaxValidatorSet::get();
167
168	/// In each page of the election, we allow up to all of the nominators of that page to be
169	/// present.
170	///
171	/// This in essence translates to "no limit on this as of now".
172	pub MaxBackersPerWinner: u32 = VoterSnapshotPerBlock::get();
173
174	/// Total number of backers per winner across all pages.
175	///
176	/// This in essence translates to "no limit on this as of now".
177	pub MaxBackersPerWinnerFinal: u32 = MaxElectingVoters::get();
178
179	/// Size of the exposures. This should be small enough to make the reward payouts cheap and
180	/// lightweight per-page.
181	// TODO: this is currently 512 in all networks, but 64 might yield better PoV, need to check logs.
182	pub const MaxExposurePageSize: u32 = 512;
183
184	/// Each solution is considered "better" if it is an epsilon better than the previous one.
185	pub SolutionImprovementThreshold: Perbill = Perbill::from_rational(1u32, 10_000);
186}
187
188// Signed phase parameters.
189parameter_types! {
190	/// * Polkadot: 16
191	/// * Kusama: 8
192	///
193	/// Reasoning: This is double the capacity of verification. There is no point for someone to be
194	/// a submitter if they cannot be verified, yet, it is beneficial to act as a "reserve", in case
195	/// someone bails out last minute.
196	pub MaxSubmissions: u32 = 8;
197
198	/// * Polkadot: Geometric progression with starting value 4 DOT, common factor 2. For 16
199	///   submissions, it will be [4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384,
200	///   32768, 65536, 131072]. Sum is `262140 DOT` for all 16 submissions.
201	/// * Kusama: Geometric progression with with starting value 0.1 KSM, common factor 4. For 8
202	///   submissions, values will be: `[0.1, 0.4, 1.6, 6.4, 25.6, 102.4, 409.6, 1638.4]`. Sum is
203	///   `2184.5 KSM` for all 8 submissions.
204	pub DepositBase: Balance = 5 * UNITS;
205
206	/// * Polkadot: standard byte deposit configured in PAH.
207	/// * Kusama: standard byte deposit configured in KAH.
208	///
209	/// TODO: need a maximum solution length for each runtime.
210	pub DepositPerPage: Balance = 1 * UNITS;
211
212	/// * Polkadot: 20 DOT
213	/// * Kusama: 1 KSM
214	///
215	///
216	/// Fixed deposit for invulnerable accounts.
217	pub InvulnerableDeposit: Balance = UNITS;
218
219	/// * Polkadot: 10% (more restrictive, don't bail!)
220	/// * Kusama: 25%
221	///
222	/// Reasoning: The weight/fee of the `bail` transaction is already assuming you delete all pages
223	/// of your solution while bailing, and charges you accordingly. So the chain is being
224	/// compensated. The risk would be for an attacker to submit a lot of high score pages, and bail
225	/// at the end to avoid getting slashed.
226	pub BailoutGraceRatio: Perbill = Perbill::from_percent(5);
227
228	/// * Polkadot: 100%
229	/// * Kusama: 100%
230	///
231	/// The transaction fee of `register` takes into account the cost of possibly ejecting another
232	/// submission into account. In the scenario that the honest submitter is being ejected by an
233	/// attacker, the cost is on the attacker, and having 100% grace ratio here is only to the
234	/// benefit of the honest submitter.
235	pub EjectGraceRatio: Perbill = Perbill::from_percent(50);
236
237	/// * Polkadot: 5 DOTs per era/day
238	/// * Kusama: 1 KSM per era/6h
239	pub RewardBase: Balance = 10 * UNITS;
240}
241
242// * Polkadot: as seen here.
243// * Kusama, we will use a similar type, but with 24 as the maximum filed length.
244//
245// Reasoning: using u16, we can have up to 65,536 nominators and validators represented in the
246// snapshot. If we every go beyond this, we have to first adjust this type.
247frame_election_provider_support::generate_solution_type!(
248	#[compact]
249	pub struct NposCompactSolution16::<
250		VoterIndex = u16,
251		TargetIndex = u16,
252		Accuracy = sp_runtime::PerU16,
253		MaxVoters = VoterSnapshotPerBlock,
254	>(16)
255);
256
257#[cfg(feature = "runtime-benchmarks")]
258parameter_types! {
259	pub BenchElectionBounds: frame_election_provider_support::bounds::ElectionBounds =
260		frame_election_provider_support::bounds::ElectionBoundsBuilder::default().build();
261}
262
263#[cfg(feature = "runtime-benchmarks")]
264pub struct OnChainConfig;
265
266#[cfg(feature = "runtime-benchmarks")]
267impl frame_election_provider_support::onchain::Config for OnChainConfig {
268	// unbounded
269	type Bounds = BenchElectionBounds;
270	// We should not need sorting, as our bounds are large enough for the number of
271	// nominators/validators in this test setup.
272	type Sort = ConstBool<false>;
273	type DataProvider = Staking;
274	type MaxBackersPerWinner = MaxBackersPerWinner;
275	type MaxWinnersPerPage = MaxWinnersPerPage;
276	type Solver = frame_election_provider_support::SequentialPhragmen<AccountId, Perbill>;
277	type System = Runtime;
278	type WeightInfo = ();
279}
280
281impl multi_block::Config for Runtime {
282	type AreWeDone = multi_block::RevertToSignedIfNotQueuedOf<Self>;
283	type Pages = Pages;
284	type UnsignedPhase = UnsignedPhase;
285	type SignedPhase = SignedPhase;
286	type SignedValidationPhase = SignedValidationPhase;
287	type VoterSnapshotPerBlock = VoterSnapshotPerBlock;
288	type TargetSnapshotPerBlock = TargetSnapshotPerBlock;
289	type AdminOrigin = EnsureRoot<AccountId>;
290	type DataProvider = Staking;
291	#[cfg(not(feature = "runtime-benchmarks"))]
292	type Fallback = multi_block::Continue<Self>;
293	#[cfg(feature = "runtime-benchmarks")]
294	type Fallback = frame_election_provider_support::onchain::OnChainExecution<OnChainConfig>;
295	type MinerConfig = Self;
296	type Verifier = MultiBlockElectionVerifier;
297	type OnRoundRotation = multi_block::CleanRound<Self>;
298	type WeightInfo = multi_block::weights::polkadot::MultiBlockWeightInfo<Self>;
299}
300
301impl multi_block::verifier::Config for Runtime {
302	type MaxWinnersPerPage = MaxWinnersPerPage;
303	type MaxBackersPerWinner = MaxBackersPerWinner;
304	type MaxBackersPerWinnerFinal = MaxBackersPerWinnerFinal;
305	type SolutionDataProvider = MultiBlockElectionSigned;
306	type SolutionImprovementThreshold = SolutionImprovementThreshold;
307	type WeightInfo = multi_block::weights::polkadot::MultiBlockVerifierWeightInfo<Self>;
308}
309
310impl multi_block::signed::Config for Runtime {
311	type Currency = Balances;
312	type BailoutGraceRatio = BailoutGraceRatio;
313	type EjectGraceRatio = EjectGraceRatio;
314	type DepositBase = DepositBase;
315	type DepositPerPage = DepositPerPage;
316	type InvulnerableDeposit = InvulnerableDeposit;
317	type RewardBase = RewardBase;
318	type MaxSubmissions = MaxSubmissions;
319	type EstimateCallFee = TransactionPayment;
320	type WeightInfo = multi_block::weights::polkadot::MultiBlockSignedWeightInfo<Self>;
321}
322
323parameter_types! {
324	/// Priority of the offchain miner transactions.
325	pub MinerTxPriority: TransactionPriority = TransactionPriority::max_value() / 2;
326}
327
328pub struct Balancing;
329impl Get<Option<BalancingConfig>> for Balancing {
330	fn get() -> Option<BalancingConfig> {
331		Some(BalancingConfig { iterations: 10, tolerance: 0 })
332	}
333}
334
335impl multi_block::unsigned::Config for Runtime {
336	type MinerPages = MinerPages;
337	type OffchainSolver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Runtime>, Balancing>;
338	type MinerTxPriority = MinerTxPriority;
339	type OffchainRepeat = OffchainRepeat;
340	type OffchainStorage = ConstBool<true>;
341	type WeightInfo = multi_block::weights::polkadot::MultiBlockUnsignedWeightInfo<Self>;
342}
343
344parameter_types! {
345	/// Miner transaction can fill up to 75% of the block size.
346	pub MinerMaxLength: u32 = Perbill::from_rational(75u32, 100) *
347		*RuntimeBlockLength::get()
348		.max
349		.get(DispatchClass::Normal);
350}
351
352impl multi_block::unsigned::miner::MinerConfig for Runtime {
353	type AccountId = AccountId;
354	type Hash = Hash;
355	type MaxBackersPerWinner = <Self as multi_block::verifier::Config>::MaxBackersPerWinner;
356	type MaxBackersPerWinnerFinal =
357		<Self as multi_block::verifier::Config>::MaxBackersPerWinnerFinal;
358	type MaxWinnersPerPage = <Self as multi_block::verifier::Config>::MaxWinnersPerPage;
359	type MaxVotesPerVoter =
360		<<Self as multi_block::Config>::DataProvider as ElectionDataProvider>::MaxVotesPerVoter;
361	type MaxLength = MinerMaxLength;
362	type Solver = <Runtime as multi_block::unsigned::Config>::OffchainSolver;
363	type Pages = Pages;
364	type Solution = NposCompactSolution16;
365	type VoterSnapshotPerBlock = <Runtime as multi_block::Config>::VoterSnapshotPerBlock;
366	type TargetSnapshotPerBlock = <Runtime as multi_block::Config>::TargetSnapshotPerBlock;
367}
368
369parameter_types! {
370	pub const BagThresholds: &'static [u64] = &bag_thresholds::THRESHOLDS;
371}
372
373type VoterBagsListInstance = pallet_bags_list::Instance1;
374impl pallet_bags_list::Config<VoterBagsListInstance> for Runtime {
375	type RuntimeEvent = RuntimeEvent;
376	type ScoreProvider = Staking;
377	type WeightInfo = weights::pallet_bags_list::WeightInfo<Runtime>;
378	type BagThresholds = BagThresholds;
379	type Score = sp_npos_elections::VoteWeight;
380	type MaxAutoRebagPerBlock = ();
381}
382
383pub struct EraPayout;
384impl pallet_staking_async::EraPayout<Balance> for EraPayout {
385	fn era_payout(
386		_total_staked: Balance,
387		_total_issuance: Balance,
388		era_duration_millis: u64,
389	) -> (Balance, Balance) {
390		const MILLISECONDS_PER_YEAR: u64 = (1000 * 3600 * 24 * 36525) / 100;
391		// A normal-sized era will have 1 / 365.25 here:
392		let relative_era_len =
393			FixedU128::from_rational(era_duration_millis.into(), MILLISECONDS_PER_YEAR.into());
394
395		// Fixed total TI that we use as baseline for the issuance.
396		let fixed_total_issuance: i128 = 5_216_342_402_773_185_773;
397		let fixed_inflation_rate = FixedU128::from_rational(8, 100);
398		let yearly_emission = fixed_inflation_rate.saturating_mul_int(fixed_total_issuance);
399
400		let era_emission = relative_era_len.saturating_mul_int(yearly_emission);
401		// 15% to treasury, as per Polkadot ref 1139.
402		let to_treasury = FixedU128::from_rational(15, 100).saturating_mul_int(era_emission);
403		let to_stakers = era_emission.saturating_sub(to_treasury);
404
405		(to_stakers.saturated_into(), to_treasury.saturated_into())
406	}
407}
408
409parameter_types! {
410	// Six sessions in an era (6 hours).
411	pub const SessionsPerEra: SessionIndex = prod_or_fast!(6, 1);
412	/// Duration of a relay session in our blocks. Needs to be hardcoded per-runtime.
413	pub const RelaySessionDuration: BlockNumber = 10;
414	// 2 eras for unbonding (12 hours).
415	pub const BondingDuration: sp_staking::EraIndex = 2;
416	// 1 era in which slashes can be cancelled (6 hours).
417	pub const SlashDeferDuration: sp_staking::EraIndex = 1;
418	// Note: this is not really correct as Max Nominators is (MaxExposurePageSize * page_count) but
419	// this is an unbounded number. We just set it to a reasonably high value, 1 full page
420	// of nominators.
421	pub const MaxControllersInDeprecationBatch: u32 = 751;
422	pub const MaxNominations: u32 = <NposCompactSolution16 as frame_election_provider_support::NposSolution>::LIMIT as u32;
423	// Note: In WAH, this should be set closer to the ideal era duration to trigger capping more
424	// frequently. On Kusama and Polkadot, a higher value like 7 × ideal_era_duration is more
425	// appropriate.
426	pub const MaxEraDuration: u64 = RelaySessionDuration::get() as u64 * RELAY_CHAIN_SLOT_DURATION_MILLIS as u64 * SessionsPerEra::get() as u64;
427	pub MaxPruningItems: u32 = 100;
428}
429
430impl pallet_staking_async::Config for Runtime {
431	type Filter = ();
432	type OldCurrency = Balances;
433	type Currency = Balances;
434	type CurrencyBalance = Balance;
435	type RuntimeHoldReason = RuntimeHoldReason;
436	type CurrencyToVote = sp_staking::currency_to_vote::SaturatingCurrencyToVote;
437	type RewardRemainder = ();
438	type Slash = ();
439	type Reward = ();
440	type SessionsPerEra = SessionsPerEra;
441	type BondingDuration = BondingDuration;
442	type SlashDeferDuration = SlashDeferDuration;
443	type AdminOrigin = EitherOf<EnsureRoot<AccountId>, StakingAdmin>;
444	type EraPayout = EraPayout;
445	type MaxExposurePageSize = MaxExposurePageSize;
446	type ElectionProvider = MultiBlockElection;
447	type VoterList = VoterList;
448	type TargetList = UseValidatorsMap<Self>;
449	type MaxValidatorSet = MaxValidatorSet;
450	type NominationsQuota = pallet_staking_async::FixedNominationsQuota<{ MaxNominations::get() }>;
451	type MaxUnlockingChunks = frame_support::traits::ConstU32<32>;
452	type HistoryDepth = ConstU32<1>;
453	type MaxControllersInDeprecationBatch = MaxControllersInDeprecationBatch;
454	type EventListeners = (NominationPools, DelegatedStaking);
455	type WeightInfo = pallet_staking_async::weights::SubstrateWeight<Runtime>;
456	type MaxInvulnerables = frame_support::traits::ConstU32<20>;
457	type MaxEraDuration = MaxEraDuration;
458	type MaxPruningItems = MaxPruningItems;
459	type PlanningEraOffset =
460		pallet_staking_async::PlanningEraOffsetOf<Self, RelaySessionDuration, ConstU32<10>>;
461	type RcClientInterface = StakingRcClient;
462}
463
464impl pallet_staking_async_rc_client::Config for Runtime {
465	type RelayChainOrigin = EnsureRoot<AccountId>;
466	type AHStakingInterface = Staking;
467	type SendToRelayChain = StakingXcmToRelayChain;
468	type MaxValidatorSetRetries = ConstU32<5>;
469}
470
471parameter_types! {
472	pub StakingXcmDestination: Location = Location::parent();
473}
474
475#[derive(Encode, Decode)]
476// Call indices taken from westend-next runtime.
477pub enum RelayChainRuntimePallets {
478	#[codec(index = 67)]
479	AhClient(AhClientCalls),
480}
481
482#[derive(Encode, Decode)]
483pub enum AhClientCalls {
484	#[codec(index = 0)]
485	ValidatorSet(rc_client::ValidatorSetReport<AccountId>),
486}
487
488pub struct ValidatorSetToXcm;
489impl Convert<rc_client::ValidatorSetReport<AccountId>, Xcm<()>> for ValidatorSetToXcm {
490	fn convert(report: rc_client::ValidatorSetReport<AccountId>) -> Xcm<()> {
491		Xcm(vec![
492			Instruction::UnpaidExecution {
493				weight_limit: WeightLimit::Unlimited,
494				check_origin: None,
495			},
496			Instruction::Transact {
497				origin_kind: OriginKind::Native,
498				fallback_max_weight: None,
499				call: RelayChainRuntimePallets::AhClient(AhClientCalls::ValidatorSet(report))
500					.encode()
501					.into(),
502			},
503		])
504	}
505}
506
507pub struct StakingXcmToRelayChain;
508
509impl rc_client::SendToRelayChain for StakingXcmToRelayChain {
510	type AccountId = AccountId;
511	fn validator_set(report: rc_client::ValidatorSetReport<Self::AccountId>) -> Result<(), ()> {
512		rc_client::XCMSender::<
513			xcm_config::XcmRouter,
514			StakingXcmDestination,
515			rc_client::ValidatorSetReport<Self::AccountId>,
516			ValidatorSetToXcm,
517		>::send(report)
518	}
519}
520
521impl pallet_fast_unstake::Config for Runtime {
522	type RuntimeEvent = RuntimeEvent;
523	type Currency = Balances;
524	type BatchSize = ConstU32<64>;
525	type Deposit = ConstU128<{ UNITS }>;
526	type ControlOrigin = EnsureRoot<AccountId>;
527	type Staking = Staking;
528	type MaxErasToCheckPerBlock = ConstU32<1>;
529	type WeightInfo = weights::pallet_fast_unstake::WeightInfo<Runtime>;
530}
531
532parameter_types! {
533	pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls");
534	pub const MaxPointsToBalance: u8 = 10;
535}
536
537impl pallet_nomination_pools::Config for Runtime {
538	type Filter = ();
539	type RuntimeEvent = RuntimeEvent;
540	type WeightInfo = weights::pallet_nomination_pools::WeightInfo<Self>;
541	type Currency = Balances;
542	type RuntimeFreezeReason = RuntimeFreezeReason;
543	type RewardCounter = FixedU128;
544	type BalanceToU256 = BalanceToU256;
545	type U256ToBalance = U256ToBalance;
546	type StakeAdapter =
547		pallet_nomination_pools::adapter::DelegateStake<Self, Staking, DelegatedStaking>;
548	type PostUnbondingPoolsWindow = ConstU32<4>;
549	type MaxMetadataLen = ConstU32<256>;
550	// we use the same number of allowed unlocking chunks as with staking.
551	type MaxUnbonding = <Self as pallet_staking_async::Config>::MaxUnlockingChunks;
552	type PalletId = PoolsPalletId;
553	type MaxPointsToBalance = MaxPointsToBalance;
554	type AdminOrigin = EitherOf<EnsureRoot<AccountId>, StakingAdmin>;
555	type BlockNumberProvider = RelayChainBlockNumberProvider;
556}
557
558parameter_types! {
559	pub const DelegatedStakingPalletId: PalletId = PalletId(*b"py/dlstk");
560	pub const SlashRewardFraction: Perbill = Perbill::from_percent(1);
561}
562
563impl pallet_delegated_staking::Config for Runtime {
564	type RuntimeEvent = RuntimeEvent;
565	type PalletId = DelegatedStakingPalletId;
566	type Currency = Balances;
567	type OnSlash = ();
568	type SlashRewardFraction = SlashRewardFraction;
569	type RuntimeHoldReason = RuntimeHoldReason;
570	type CoreStaking = Staking;
571}
572
573/// The payload being signed in transactions.
574pub type SignedPayload = generic::SignedPayload<RuntimeCall, TxExtension>;
575/// Unchecked extrinsic type as expected by this runtime.
576pub type UncheckedExtrinsic =
577	generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, TxExtension>;
578
579impl frame_system::offchain::SigningTypes for Runtime {
580	type Public = <Signature as Verify>::Signer;
581	type Signature = Signature;
582}
583
584impl<C> frame_system::offchain::CreateTransactionBase<C> for Runtime
585where
586	RuntimeCall: From<C>,
587{
588	type RuntimeCall = RuntimeCall;
589	type Extrinsic = UncheckedExtrinsic;
590}
591
592impl<LocalCall> frame_system::offchain::CreateTransaction<LocalCall> for Runtime
593where
594	RuntimeCall: From<LocalCall>,
595{
596	type Extension = TxExtension;
597
598	fn create_transaction(call: RuntimeCall, extension: TxExtension) -> UncheckedExtrinsic {
599		UncheckedExtrinsic::new_transaction(call, extension)
600	}
601}
602
603/// Submits a transaction with the node's public and signature type. Adheres to the signed extension
604/// format of the chain.
605impl<LocalCall> frame_system::offchain::CreateSignedTransaction<LocalCall> for Runtime
606where
607	RuntimeCall: From<LocalCall>,
608{
609	fn create_signed_transaction<
610		C: frame_system::offchain::AppCrypto<Self::Public, Self::Signature>,
611	>(
612		call: RuntimeCall,
613		public: <Signature as Verify>::Signer,
614		account: AccountId,
615		nonce: <Runtime as frame_system::Config>::Nonce,
616	) -> Option<UncheckedExtrinsic> {
617		use sp_runtime::traits::StaticLookup;
618		// take the biggest period possible.
619		let period =
620			BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64;
621
622		let current_block = System::block_number()
623			.saturated_into::<u64>()
624			// The `System::block_number` is initialized with `n+1`,
625			// so the actual block number is `n`.
626			.saturating_sub(1);
627		let tip = 0;
628		let tx_ext = TxExtension::from((
629			frame_system::CheckNonZeroSender::<Runtime>::new(),
630			frame_system::CheckSpecVersion::<Runtime>::new(),
631			frame_system::CheckTxVersion::<Runtime>::new(),
632			frame_system::CheckGenesis::<Runtime>::new(),
633			frame_system::CheckEra::<Runtime>::from(generic::Era::mortal(period, current_block)),
634			frame_system::CheckNonce::<Runtime>::from(nonce),
635			frame_system::CheckWeight::<Runtime>::new(),
636			pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::<Runtime>::from(tip, None),
637			frame_metadata_hash_extension::CheckMetadataHash::<Runtime>::new(true),
638		));
639		let raw_payload = SignedPayload::new(call, tx_ext)
640			.map_err(|e| {
641				log::warn!("Unable to create signed payload: {:?}", e);
642			})
643			.ok()?;
644		let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?;
645		let (call, tx_ext, _) = raw_payload.deconstruct();
646		let address = <Runtime as frame_system::Config>::Lookup::unlookup(account);
647		let transaction = UncheckedExtrinsic::new_signed(call, address, signature, tx_ext);
648		Some(transaction)
649	}
650}
651
652impl<LocalCall> frame_system::offchain::CreateInherent<LocalCall> for Runtime
653where
654	RuntimeCall: From<LocalCall>,
655{
656	fn create_bare(call: RuntimeCall) -> UncheckedExtrinsic {
657		UncheckedExtrinsic::new_bare(call)
658	}
659}
660
661#[cfg(test)]
662mod tests {
663	use super::*;
664	use frame_election_provider_support::ElectionProvider;
665	use frame_support::weights::constants::{WEIGHT_PROOF_SIZE_PER_KB, WEIGHT_REF_TIME_PER_MILLIS};
666	use pallet_election_provider_multi_block::{
667		self as mb, signed::WeightInfo as _, unsigned::WeightInfo as _,
668	};
669	use remote_externalities::{
670		Builder, Mode, OfflineConfig, OnlineConfig, SnapshotConfig, Transport,
671	};
672	use std::env::var;
673
674	fn weight_diff(block: Weight, op: Weight) {
675		log::info!(
676			target: "runtime",
677			"ref_time: {:?}ms {:.4} of total",
678			op.ref_time() / WEIGHT_REF_TIME_PER_MILLIS,
679			op.ref_time() as f64 / block.ref_time() as f64
680		);
681		log::info!(
682			target: "runtime",
683			"proof_size: {:?}kb {:.4} of total",
684			op.proof_size() / WEIGHT_PROOF_SIZE_PER_KB,
685			op.proof_size() as f64 / block.proof_size() as f64
686		);
687	}
688
689	#[test]
690	fn signed_weight_ratios() {
691		sp_tracing::try_init_simple();
692		let block_weight = <Runtime as frame_system::Config>::BlockWeights::get().max_block;
693		let polkadot_signed_submission =
694			mb::weights::polkadot::MultiBlockSignedWeightInfo::<Runtime>::submit_page();
695		let kusama_signed_submission =
696			mb::weights::kusama::MultiBlockSignedWeightInfo::<Runtime>::submit_page();
697
698		log::info!(target: "runtime", "Polkadot:");
699		weight_diff(block_weight, polkadot_signed_submission);
700		log::info!(target: "runtime", "Kusama:");
701		weight_diff(block_weight, kusama_signed_submission);
702	}
703
704	#[test]
705	fn election_duration() {
706		sp_tracing::try_init_simple();
707		sp_io::TestExternalities::default().execute_with(|| {
708			super::enable_dot_preset(false);
709			let duration = mb::Pallet::<Runtime>::average_election_duration();
710			let polkadot_session = 6 * HOURS;
711			log::info!(
712				target: "runtime",
713				"Polkadot election duration: {:?}, session: {:?} ({} sessions)",
714				duration,
715				polkadot_session,
716				duration / polkadot_session
717			);
718		});
719
720		sp_io::TestExternalities::default().execute_with(|| {
721			super::enable_ksm_preset(false);
722			let duration = mb::Pallet::<Runtime>::average_election_duration();
723			let kusama_session = 1 * HOURS;
724			log::info!(
725				target: "runtime",
726				"Kusama election duration: {:?}, session: {:?} ({} sessions)",
727				duration,
728				kusama_session,
729				duration / kusama_session
730			);
731		});
732	}
733
734	#[test]
735	fn max_ocw_miner_pages_as_per_weights() {
736		sp_tracing::try_init_simple();
737		for p in 1..=32 {
738			log::info!(
739				target: "runtime",
740				"exec_time of polkadot miner in WASM with {} pages is {:?}ms",
741				p,
742				mb::weights::polkadot::MultiBlockUnsignedWeightInfo::<Runtime>::mine_solution(p).ref_time() / WEIGHT_REF_TIME_PER_MILLIS
743			);
744		}
745		for p in 1..=16 {
746			log::info!(
747				target: "runtime",
748				"exec_time of kusama miner in WASM with {} pages is {:?}ms",
749				p,
750				mb::weights::kusama::MultiBlockUnsignedWeightInfo::<Runtime>::mine_solution(p).ref_time() / WEIGHT_REF_TIME_PER_MILLIS
751			);
752		}
753	}
754
755	/// Run it like:
756	///
757	/// ```text
758	/// RUST_BACKTRACE=full \
759	/// 	RUST_LOG=remote-ext=info,runtime::staking-async=debug \
760	/// 	REMOTE_TESTS=1 \
761	/// 	WS=ws://127.0.0.1:9999 \
762	/// 	cargo test --release -p pallet-staking-async-parachain-runtime \
763	/// 	--features try-runtime run_try
764	/// ```
765	///
766	/// Just replace the node with your local node.
767	///
768	/// Pass `SNAP=polkadot` or similar to store and reuse a snapshot.
769	#[tokio::test]
770	async fn run_election_with_pages() {
771		if var("REMOTE_TESTS").is_err() {
772			return;
773		}
774		sp_tracing::try_init_simple();
775
776		let transport: Transport =
777			var("WS").unwrap_or("wss://westend-rpc.polkadot.io:443".to_string()).into();
778		let maybe_state_snapshot: Option<SnapshotConfig> = var("SNAP").map(|s| s.into()).ok();
779
780		let mut ext = Builder::<Block>::default()
781			.mode(if let Some(state_snapshot) = maybe_state_snapshot {
782				Mode::OfflineOrElseOnline(
783					OfflineConfig { state_snapshot: state_snapshot.clone() },
784					OnlineConfig {
785						transport,
786						hashed_prefixes: vec![vec![]],
787						state_snapshot: Some(state_snapshot),
788						..Default::default()
789					},
790				)
791			} else {
792				Mode::Online(OnlineConfig {
793					hashed_prefixes: vec![vec![]],
794					transport,
795					..Default::default()
796				})
797			})
798			.build()
799			.await
800			.unwrap();
801		ext.execute_with(|| {
802			sp_core::crypto::set_default_ss58_version(1u8.into());
803			super::enable_dot_preset(true);
804
805			// prepare all snapshot in EPMB pallet.
806			mb::Pallet::<Runtime>::asap();
807			for page in 1..=32 {
808				mb::unsigned::miner::OffchainWorkerMiner::<Runtime>::mine_solution(page, true)
809					.inspect(|p| log::info!(target: "runtime", "{:?}", p.score.pretty("DOT", 10)))
810					.unwrap();
811			}
812		});
813	}
814}