referrerpolicy=no-referrer-when-downgrade

cumulus_test_runtime/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3
4// Cumulus is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Cumulus is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
16
17#![cfg_attr(not(feature = "std"), no_std)]
18// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
19#![recursion_limit = "256"]
20
21// Make the WASM binaries available.
22#[cfg(feature = "std")]
23include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
24
25mod features;
26mod flavors;
27mod genesis_config_presets;
28pub mod test_pallet;
29
30extern crate alloc;
31
32use features::*;
33
34use alloc::{vec, vec::Vec};
35use frame_support::{derive_impl, traits::OnRuntimeUpgrade, PalletId};
36use sp_api::{decl_runtime_apis, impl_runtime_apis};
37pub use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
38pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;
39use sp_core::{ConstBool, ConstU32, ConstU64, Get, OpaqueMetadata};
40
41use sp_runtime::{
42	generic, impl_opaque_keys,
43	traits::{BlakeTwo256, Block as BlockT, IdentifyAccount, Verify},
44	transaction_validity::{TransactionSource, TransactionValidity},
45	ApplyExtrinsicResult, MultiAddress, MultiSignature,
46};
47use sp_version::RuntimeVersion;
48
49use cumulus_primitives_core::{ParaId, RelayProofRequest, VerifySchedulingSignature};
50
51define_flavors!(consts wasm);
52
53// A few exports that help ease life for downstream crates.
54pub use frame_support::{
55	construct_runtime,
56	dispatch::DispatchClass,
57	genesis_builder_helper::{build_state, get_preset},
58	parameter_types,
59	traits::{ConstU8, Randomness},
60	weights::{
61		constants::{
62			BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND,
63		},
64		ConstantMultiplier, IdentityFee, Weight,
65	},
66	StorageValue,
67};
68pub use frame_system::Call as SystemCall;
69use frame_system::{
70	limits::{BlockLength, BlockWeights},
71	EnsureRoot,
72};
73pub use pallet_balances::Call as BalancesCall;
74pub use pallet_glutton::Call as GluttonCall;
75pub use pallet_sudo::Call as SudoCall;
76pub use pallet_timestamp::{Call as TimestampCall, Now};
77#[cfg(any(feature = "std", test))]
78pub use sp_runtime::BuildStorage;
79pub use sp_runtime::{Perbill, Permill};
80pub use test_pallet::{Call as TestPalletCall, TestTransactionExtension};
81
82pub type SessionHandlers = ();
83
84#[cfg(not(feature = "with-authority-discovery"))]
85impl_opaque_keys! {
86	pub struct SessionKeys {
87		pub aura: Aura,
88	}
89}
90
91#[cfg(feature = "with-authority-discovery")]
92impl_opaque_keys! {
93	pub struct SessionKeys {
94		pub aura: Aura,
95		pub authority_discovery: AuthorityDiscovery,
96	}
97}
98
99/// The para-id used in this runtime.
100pub const PARACHAIN_ID: u32 = 100;
101
102const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
103
104// The only difference between the three declarations below is the `spec_version`.
105// The behavior is:
106// - by default `spec_version` should be 2
107// - with `spec-version-3` feature enabled `spec_version` should be 3
108// - with `spec-version-4` feature enabled `spec_version` should be 4
109//
110// The duplication here is unfortunate necessity.
111//
112// runtime_version macro is dumb. It accepts a const item declaration, passes it through and
113// also emits runtime version custom section. It parses the expressions to extract the version
114// details. Since macro kicks in early, it operates on AST. Thus, you cannot use constants.
115// Macros are expanded top to bottom, meaning we also cannot use `cfg` here.
116#[cfg(all(not(feature = "spec-version-3"), not(feature = "spec-version-4"),))]
117#[sp_version::runtime_version]
118pub const VERSION: RuntimeVersion = RuntimeVersion {
119	spec_name: alloc::borrow::Cow::Borrowed("cumulus-test-parachain"),
120	impl_name: alloc::borrow::Cow::Borrowed("cumulus-test-parachain"),
121	authoring_version: 1,
122	// Read the note above.
123	spec_version: 2,
124	impl_version: 1,
125	apis: RUNTIME_API_VERSIONS,
126	transaction_version: 1,
127	system_version: 3,
128};
129
130#[cfg(all(feature = "spec-version-3", not(feature = "spec-version-4"),))]
131#[sp_version::runtime_version]
132pub const VERSION: RuntimeVersion = RuntimeVersion {
133	spec_name: alloc::borrow::Cow::Borrowed("cumulus-test-parachain"),
134	impl_name: alloc::borrow::Cow::Borrowed("cumulus-test-parachain"),
135	authoring_version: 1,
136	// Read the note above.
137	spec_version: 3,
138	impl_version: 1,
139	apis: RUNTIME_API_VERSIONS,
140	transaction_version: 1,
141	system_version: 3,
142};
143
144#[cfg(feature = "spec-version-4")]
145#[sp_version::runtime_version]
146pub const VERSION: RuntimeVersion = RuntimeVersion {
147	spec_name: alloc::borrow::Cow::Borrowed("cumulus-test-parachain"),
148	impl_name: alloc::borrow::Cow::Borrowed("cumulus-test-parachain"),
149	authoring_version: 1,
150	// Read the note above.
151	spec_version: 4,
152	impl_version: 1,
153	apis: RUNTIME_API_VERSIONS,
154	transaction_version: 1,
155	system_version: 3,
156};
157
158pub const EPOCH_DURATION_IN_BLOCKS: u32 = 10 * MINUTES;
159
160// These time units are defined in number of blocks.
161pub const MINUTES: BlockNumber = 60_000 / (slot_duration() as BlockNumber);
162pub const HOURS: BlockNumber = MINUTES * 60;
163pub const DAYS: BlockNumber = HOURS * 24;
164
165// 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks.
166pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4);
167
168/// We assume that ~10% of the block weight is consumed by `on_initialize` handlers.
169/// This is used to limit the maximal weight of a single extrinsic.
170const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10);
171/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used
172/// by  Operational  extrinsics.
173const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
174
175type MaximumBlockWeight = cumulus_pallet_parachain_system::block_weight::MaxParachainBlockWeight<
176	Runtime,
177	ConstU32<{ block_processing_velocity() }>,
178>;
179
180parameter_types! {
181	/// Target number of blocks per relay chain slot.
182	pub const NumberOfBlocksPerRelaySlot: u32 = 12;
183	pub const BlockHashCount: BlockNumber = 250;
184	pub const Version: RuntimeVersion = VERSION;
185	/// We allow for 1 second of compute with a 6 second average block time.
186	pub RuntimeBlockLength: BlockLength =
187		BlockLength::builder().max_length(10 * 1024 * 1024).max_header_size(5 * 1024 * 1024).build();
188	pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder()
189		.base_block(BlockExecutionWeight::get())
190		.for_class(DispatchClass::all(), |weights| {
191			weights.base_extrinsic = ExtrinsicBaseWeight::get();
192		})
193		.for_class(DispatchClass::Normal, |weights| {
194			weights.max_total = Some(NORMAL_DISPATCH_RATIO * MaximumBlockWeight::get());
195		})
196		.for_class(DispatchClass::Operational, |weights| {
197			weights.max_total = Some(MaximumBlockWeight::get());
198			// Operational transactions have some extra reserved space, so that they
199			// are included even if block reached `MaximumBlockWeight`.
200			weights.reserved = Some(
201				MaximumBlockWeight::get() - NORMAL_DISPATCH_RATIO * MaximumBlockWeight::get()
202			);
203		})
204		.avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)
205		.build_or_panic();
206	pub const SS58Prefix: u8 = 42;
207}
208
209#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)]
210impl frame_system::Config for Runtime {
211	/// The identifier used to distinguish between accounts.
212	type AccountId = AccountId;
213	/// The index type for storing how many extrinsics an account has signed.
214	type Nonce = Nonce;
215	/// The type for hashing blocks and tries.
216	type Hash = Hash;
217	/// The block type.
218	type Block = Block;
219	/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
220	type BlockHashCount = BlockHashCount;
221	/// Runtime version.
222	type Version = Version;
223	type AccountData = pallet_balances::AccountData<Balance>;
224	type BlockWeights = RuntimeBlockWeights;
225	type BlockLength = RuntimeBlockLength;
226	type SS58Prefix = SS58Prefix;
227	type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>;
228	type MaxConsumers = frame_support::traits::ConstU32<16>;
229	type PreInherents = cumulus_pallet_parachain_system::block_weight::DynamicMaxBlockWeightHooks<
230		Runtime,
231		ConstU32<{ block_processing_velocity() }>,
232	>;
233	type SingleBlockMigrations = SingleBlockMigrations;
234}
235
236impl cumulus_pallet_weight_reclaim::Config for Runtime {
237	type WeightInfo = ();
238}
239
240parameter_types! {
241	pub const MinimumPeriod: u64 = 0;
242}
243
244parameter_types! {
245	pub const PotId: PalletId = PalletId(*b"PotStake");
246	pub const SessionLength: BlockNumber = 10 * MINUTES;
247	pub const Offset: u32 = 0;
248}
249
250impl cumulus_pallet_aura_ext::Config for Runtime {}
251
252impl pallet_timestamp::Config for Runtime {
253	/// A timestamp: milliseconds since the unix epoch.
254	type Moment = u64;
255	type OnTimestampSet = Aura;
256	type MinimumPeriod = MinimumPeriod;
257	type WeightInfo = ();
258}
259
260parameter_types! {
261	pub const ExistentialDeposit: u128 = 500;
262	pub const TransferFee: u128 = 0;
263	pub const CreationFee: u128 = 0;
264	pub const TransactionByteFee: u128 = 1;
265	pub const MaxReserves: u32 = 50;
266}
267
268impl pallet_balances::Config for Runtime {
269	/// The type for recording an account's balance.
270	type Balance = Balance;
271	/// The ubiquitous event type.
272	type RuntimeEvent = RuntimeEvent;
273	type DustRemoval = ();
274	type ExistentialDeposit = ExistentialDeposit;
275	type AccountStore = System;
276	type WeightInfo = ();
277	type MaxLocks = ();
278	type MaxReserves = MaxReserves;
279	type ReserveIdentifier = [u8; 8];
280	type RuntimeHoldReason = RuntimeHoldReason;
281	type RuntimeFreezeReason = RuntimeFreezeReason;
282	type FreezeIdentifier = ();
283	type MaxFreezes = ConstU32<0>;
284	type DoneSlashHandler = ();
285}
286
287impl pallet_transaction_payment::Config for Runtime {
288	type RuntimeEvent = RuntimeEvent;
289	type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter<Balances, ()>;
290	type WeightToFee = IdentityFee<Balance>;
291	type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
292	type FeeMultiplierUpdate = ();
293	type OperationalFeeMultiplier = ConstU8<5>;
294	type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight<Runtime>;
295}
296
297impl pallet_sudo::Config for Runtime {
298	type RuntimeCall = RuntimeCall;
299	type RuntimeEvent = RuntimeEvent;
300	type WeightInfo = pallet_sudo::weights::SubstrateWeight<Runtime>;
301}
302
303impl pallet_utility::Config for Runtime {
304	type RuntimeCall = RuntimeCall;
305	type RuntimeEvent = RuntimeEvent;
306	type PalletsOrigin = OriginCaller;
307	type WeightInfo = pallet_utility::weights::SubstrateWeight<Runtime>;
308}
309
310impl pallet_glutton::Config for Runtime {
311	type RuntimeEvent = RuntimeEvent;
312	type AdminOrigin = EnsureRoot<AccountId>;
313	type WeightInfo = pallet_glutton::weights::SubstrateWeight<Runtime>;
314}
315
316/// Scheduling-info verifier used by `cumulus-test-runtime`.
317///
318/// Accepts any signature; `V3_SCHEDULING_ENABLED` is gated on the `v3-descriptor` cargo
319/// feature so the test runtime can flip V3 scheduling on without needing a runtime upgrade
320/// per build.
321pub struct NoVerification;
322
323impl VerifySchedulingSignature for NoVerification {
324	const V3_SCHEDULING_ENABLED: bool = SCHEDULING_V3_ENABLED;
325
326	fn verify(
327		_signed_info: &cumulus_primitives_core::SignedSchedulingInfo,
328		_relay_slot: cumulus_primitives_core::relay_chain::Slot,
329	) -> bool {
330		true
331	}
332}
333
334type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook<
335	Runtime,
336	RELAY_CHAIN_SLOT_DURATION_MILLIS,
337	{ block_processing_velocity() },
338	{ unincluded_segment_capacity() },
339>;
340impl cumulus_pallet_parachain_system::Config for Runtime {
341	type WeightInfo = ();
342	type SelfParaId = parachain_info::Pallet<Runtime>;
343	type RuntimeEvent = RuntimeEvent;
344	type OnSystemEvent = TestPallet;
345	type OutboundXcmpMessageSource = TestPallet;
346	// Ignore all DMP messages by enqueueing them into `()`:
347	type DmpQueue = frame_support::traits::EnqueueWithOrigin<(), sp_core::ConstU8<0>>;
348	type ReservedDmpWeight = ();
349	type XcmpMessageHandler = ();
350	type ReservedXcmpWeight = ();
351	type CheckAssociatedRelayNumber =
352		cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
353	type ConsensusHook = ConsensusHook;
354	type RelayParentOffset = ConstU32<{ relay_parent_offset() }>;
355	type SchedulingSignatureVerifier = NoVerification;
356}
357
358impl parachain_info::Config for Runtime {}
359
360impl pallet_aura::Config for Runtime {
361	type AuthorityId = AuraId;
362	type DisabledValidators = ();
363	type MaxAuthorities = ConstU32<32>;
364	type AllowMultipleBlocksPerSlot = ConstBool<{ !cfg!(feature = "sync-backing") }>;
365	type SlotDuration = ConstU64<{ slot_duration() }>;
366}
367
368impl test_pallet::Config for Runtime {}
369
370parameter_types! {
371	pub const Period: u32 = 10;
372}
373
374#[cfg(feature = "with-authority-discovery")]
375impl pallet_session::Config for Runtime {
376	type RuntimeEvent = RuntimeEvent;
377	type ValidatorId = AccountId;
378	type ValidatorIdOf = sp_runtime::traits::ConvertInto;
379	type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
380	type NextSessionRotation = pallet_session::PeriodicSessions<Period, Offset>;
381	type SessionManager = ();
382	type SessionHandler = <SessionKeys as sp_runtime::traits::OpaqueKeys>::KeyTypeIdProviders;
383	type Keys = SessionKeys;
384	type DisablingStrategy = ();
385	type WeightInfo = ();
386	type Currency = Balances;
387	type KeyDeposit = ();
388}
389
390#[cfg(feature = "with-authority-discovery")]
391impl pallet_authority_discovery::Config for Runtime {
392	type MaxAuthorities = ConstU32<32>;
393}
394
395construct_runtime! {
396	pub enum Runtime
397	{
398		System: frame_system,
399		ParachainSystem: cumulus_pallet_parachain_system,
400		Timestamp: pallet_timestamp,
401		ParachainInfo: parachain_info,
402		Balances: pallet_balances,
403		Sudo: pallet_sudo,
404		Utility: pallet_utility,
405		TransactionPayment: pallet_transaction_payment,
406		TestPallet: test_pallet,
407		Glutton: pallet_glutton,
408		Aura: pallet_aura,
409		// Session must come BEFORE AuraExt so its on_genesis_session populates
410		// pallet_aura::Authorities before AuraExt's genesis_build snapshots it.
411		#[cfg(feature = "with-authority-discovery")]
412		Session: pallet_session,
413		#[cfg(feature = "with-authority-discovery")]
414		AuthorityDiscovery: pallet_authority_discovery,
415		AuraExt: cumulus_pallet_aura_ext,
416		WeightReclaim: cumulus_pallet_weight_reclaim,
417	}
418}
419
420/// Index of a transaction in the chain.
421pub type Nonce = u32;
422/// A hash of some data used by the chain.
423pub type Hash = sp_core::H256;
424/// Balance of an account.
425pub type Balance = u128;
426/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
427pub type Signature = MultiSignature;
428/// An index to a block.
429pub type BlockNumber = u32;
430/// Some way of identifying an account on the chain. We intentionally make it equivalent
431/// to the public key of our transaction signing scheme.
432pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
433/// Opaque block type.
434pub type NodeBlock = generic::Block<Header, sp_runtime::OpaqueExtrinsic>;
435
436/// The address format for describing accounts.
437pub type Address = MultiAddress<AccountId, ()>;
438/// Block header type as expected by this runtime.
439pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
440/// Block type as expected by this runtime.
441pub type Block = generic::Block<Header, UncheckedExtrinsic>;
442/// A Block signed with a Justification
443pub type SignedBlock = generic::SignedBlock<Block>;
444/// BlockId type as expected by this runtime.
445pub type BlockId = generic::BlockId<Block>;
446/// The extension to the basic transaction logic.
447pub type TxExtension = cumulus_pallet_parachain_system::block_weight::DynamicMaxBlockWeight<
448	Runtime,
449	cumulus_pallet_weight_reclaim::StorageWeightReclaim<
450		Runtime,
451		(
452			frame_system::AuthorizeCall<Runtime>,
453			frame_system::CheckNonZeroSender<Runtime>,
454			frame_system::CheckSpecVersion<Runtime>,
455			frame_system::CheckGenesis<Runtime>,
456			frame_system::CheckEra<Runtime>,
457			frame_system::CheckNonce<Runtime>,
458			frame_system::CheckWeight<Runtime>,
459			pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
460			test_pallet::TestTransactionExtension<Runtime>,
461		),
462	>,
463	ConstU32<{ block_processing_velocity() }>,
464>;
465
466/// Unchecked extrinsic type as expected by this runtime.
467pub type UncheckedExtrinsic =
468	generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, TxExtension>;
469/// Executive: handles dispatch to the various modules.
470pub type Executive = frame_executive::Executive<
471	Runtime,
472	Block,
473	frame_system::ChainContext<Runtime>,
474	Runtime,
475	AllPalletsWithSystem,
476>;
477
478/// The payload being signed in transactions.
479pub type SignedPayload = generic::SignedPayload<RuntimeCall, TxExtension>;
480
481/// Migration to verify that runtime upgrade hooks are working correctly.
482///
483/// This checks that the test_pallet runtime upgrade key was set in genesis.
484pub struct VerifyRuntimeUpgrade;
485
486impl OnRuntimeUpgrade for VerifyRuntimeUpgrade {
487	fn on_runtime_upgrade() -> Weight {
488		assert_eq!(
489			sp_io::storage::get(test_pallet::TEST_RUNTIME_UPGRADE_KEY),
490			Some(vec![1, 2, 3, 4].into())
491		);
492		Weight::from_parts(1, 0)
493	}
494}
495
496/// Single-block migrations for the test runtime.
497///
498/// These migrations execute immediately and entirely at the beginning of the block following
499/// a runtime upgrade. They must be lightweight enough to complete within a single block.
500#[cfg(feature = "with-authority-discovery")]
501pub type SingleBlockMigrations = (VerifyRuntimeUpgrade, migrations::EnableAuthorityDiscovery);
502#[cfg(not(feature = "with-authority-discovery"))]
503pub type SingleBlockMigrations = (VerifyRuntimeUpgrade,);
504
505/// One-shot migration that seeds `pallet_session` from `pallet_aura::Authorities` when a
506/// default (no-AD) chain upgrades to the `with-authority-discovery` variant.
507///
508/// Idempotent: only runs when `pallet_session::Validators` is empty, which is the case
509/// on a chain that never had `pallet_session` in its runtime.
510#[cfg(feature = "with-authority-discovery")]
511pub mod migrations {
512	use super::*;
513	use sp_core::crypto::key_types;
514
515	pub struct EnableAuthorityDiscovery;
516
517	impl OnRuntimeUpgrade for EnableAuthorityDiscovery {
518		fn on_runtime_upgrade() -> Weight {
519			let db: frame_support::weights::RuntimeDbWeight =
520				<Runtime as frame_system::Config>::DbWeight::get();
521
522			// Idempotent guard: skip if Validators is already populated.
523			if !pallet_session::Validators::<Runtime>::get().is_empty() {
524				return db.reads(1);
525			}
526
527			let aura_authorities = pallet_aura::Authorities::<Runtime>::get();
528			let n = aura_authorities.len() as u64;
529
530			let mut validators: Vec<AccountId> = Vec::with_capacity(aura_authorities.len());
531			let mut queued_keys: Vec<(AccountId, SessionKeys)> =
532				Vec::with_capacity(aura_authorities.len());
533
534			for aura_pub in aura_authorities.iter() {
535				// `AuraId` is app-crypto over `sr25519::Public`; `.into()` gives the inner.
536				let inner: sp_core::sr25519::Public = aura_pub.clone().into();
537				let raw: [u8; 32] = inner.0;
538				let account: AccountId = sp_core::sr25519::Public::from_raw(raw).into();
539				let aura_key = AuraId::from(sp_core::sr25519::Public::from_raw(raw));
540				let audi_key = AuthorityDiscoveryId::from(sp_core::sr25519::Public::from_raw(raw));
541				let session_keys = SessionKeys { aura: aura_key, authority_discovery: audi_key };
542
543				// Populate NextKeys and KeyOwner (mirrors pallet_session genesis logic).
544				pallet_session::NextKeys::<Runtime>::insert(&account, &session_keys);
545				// KeyOwner maps (KeyTypeId, key_bytes: Vec<u8>) → ValidatorId.
546				// We use <[u8]>::to_vec() to get an owned Vec<u8> that EncodeLike<Vec<u8>>.
547				let aura_bytes: alloc::vec::Vec<u8> =
548					<AuraId as sp_runtime::RuntimeAppPublic>::to_raw_vec(&session_keys.aura);
549				let audi_bytes: alloc::vec::Vec<u8> =
550					<AuthorityDiscoveryId as sp_runtime::RuntimeAppPublic>::to_raw_vec(
551						&session_keys.authority_discovery,
552					);
553				pallet_session::KeyOwner::<Runtime>::insert(
554					(key_types::AURA, aura_bytes),
555					&account,
556				);
557				pallet_session::KeyOwner::<Runtime>::insert(
558					(key_types::AUTHORITY_DISCOVERY, audi_bytes),
559					&account,
560				);
561
562				// Mirror `pallet_session::do_set_keys`: increment the account's consumer
563				// count so a future `purge_keys` decrements it correctly. Zombienet-injected
564				// aura keys without endowment are skipped — they have no consumer to track.
565				if frame_system::Pallet::<Runtime>::providers(&account) > 0 {
566					let inc_ok = frame_system::Pallet::<Runtime>::inc_consumers(&account).is_ok();
567					debug_assert!(inc_ok, "inc_consumers failed despite providers > 0");
568				}
569
570				validators.push(account.clone());
571				queued_keys.push((account, session_keys));
572			}
573
574			// Write Validators and QueuedKeys so the session pallet has a coherent state.
575			pallet_session::Validators::<Runtime>::put(&validators);
576			pallet_session::QueuedKeys::<Runtime>::put(&queued_keys);
577
578			// YOLO so these keys are not empty until next session.
579			let ad_authorities: Vec<AuthorityDiscoveryId> = aura_authorities
580				.iter()
581				.map(|aura_pub| {
582					let inner: sp_core::sr25519::Public = aura_pub.clone().into();
583					AuthorityDiscoveryId::from(sp_core::sr25519::Public::from_raw(inner.0))
584				})
585				.collect();
586			let bounded = frame_support::WeakBoundedVec::<_, _>::force_from(
587				ad_authorities,
588				Some("EnableAuthorityDiscovery migration: authority count exceeds MaxAuthorities"),
589			);
590			pallet_authority_discovery::Keys::<Runtime>::put(bounded);
591
592			Self::assert_post_upgrade_invariants();
593
594			let reads = n.saturating_add(2);
595			let writes = n.saturating_mul(4).saturating_add(3);
596			db.reads(reads).saturating_add(db.writes(writes))
597		}
598	}
599
600	impl EnableAuthorityDiscovery {
601		fn assert_post_upgrade_invariants() {
602			let aura_count = pallet_aura::Authorities::<Runtime>::get().len();
603			let validators = pallet_session::Validators::<Runtime>::get();
604			let queued = pallet_session::QueuedKeys::<Runtime>::get();
605			let ad_keys = pallet_authority_discovery::Keys::<Runtime>::get();
606
607			assert!(!validators.is_empty(), "Validators empty after migration");
608			assert_eq!(validators.len(), aura_count, "Validators ≠ aura Authorities");
609			assert_eq!(queued.len(), aura_count, "QueuedKeys ≠ aura Authorities");
610			assert_eq!(ad_keys.len(), aura_count, "AuthorityDiscovery::Keys ≠ aura Authorities");
611			assert_eq!(
612				pallet_session::NextKeys::<Runtime>::iter().count(),
613				aura_count,
614				"NextKeys entry count ≠ aura Authorities",
615			);
616			assert_eq!(
617				pallet_session::KeyOwner::<Runtime>::iter().count(),
618				2 * aura_count,
619				"KeyOwner count ≠ 2× aura Authorities (aura + audi)",
620			);
621			// Each provisioned validator account had its consumer count bumped by
622			// `inc_consumers`, mirroring `pallet_session::do_set_keys` semantics.
623			// Un-provisioned aura authorities (e.g. extra zombienet-generated collator keys
624			// that aren't in the endowed-accounts list) are skipped: `inc_consumers`
625			// returned `Err` for them at migration time, and they have no consumer to bump.
626			for account in &validators {
627				if frame_system::Pallet::<Runtime>::providers(account) > 0 {
628					assert!(
629						frame_system::Pallet::<Runtime>::consumers(account) >= 1,
630						"provisioned validator {account:?} has 0 consumers; \
631						 inc_consumers didn't fire",
632					);
633				}
634			}
635		}
636	}
637
638	#[cfg(test)]
639	mod tests {
640		use super::*;
641		use frame_support::traits::OnRuntimeUpgrade;
642		use sp_keyring::Sr25519Keyring;
643
644		fn ext_with_aura(keys: &[Sr25519Keyring]) -> sp_io::TestExternalities {
645			let mut ext = sp_io::TestExternalities::new_empty();
646			ext.execute_with(|| {
647				let aura_keys: alloc::vec::Vec<AuraId> = keys
648					.iter()
649					.map(|k| AuraId::from(sp_core::sr25519::Public::from_raw(k.public().0)))
650					.collect();
651				let bounded = frame_support::BoundedVec::<_, _>::try_from(aura_keys).expect("fits");
652				pallet_aura::Authorities::<Runtime>::put(bounded);
653				// Provision providers so the migration's `inc_consumers` call can succeed —
654				// production parachains rely on every authority account being funded.
655				for k in keys {
656					let account: AccountId = k.to_account_id();
657					frame_system::Pallet::<Runtime>::inc_providers(&account);
658				}
659			});
660			ext
661		}
662
663		fn expected_weight(n: u64) -> Weight {
664			let db: frame_support::weights::RuntimeDbWeight =
665				<Runtime as frame_system::Config>::DbWeight::get();
666			let reads = n.saturating_add(2);
667			let writes = n.saturating_mul(4).saturating_add(3);
668			db.reads(reads).saturating_add(db.writes(writes))
669		}
670
671		#[test]
672		fn populates_session_state() {
673			// Invariants are asserted in `on_runtime_upgrade`.
674			let keys = [Sr25519Keyring::Alice, Sr25519Keyring::Bob, Sr25519Keyring::Charlie];
675			ext_with_aura(&keys).execute_with(|| {
676				let w = EnableAuthorityDiscovery::on_runtime_upgrade();
677				assert_eq!(w, expected_weight(keys.len() as u64));
678			});
679		}
680
681		#[test]
682		fn is_idempotent() {
683			let keys = [Sr25519Keyring::Alice, Sr25519Keyring::Bob];
684			ext_with_aura(&keys).execute_with(|| {
685				EnableAuthorityDiscovery::on_runtime_upgrade();
686				let w2 = EnableAuthorityDiscovery::on_runtime_upgrade();
687
688				let db: frame_support::weights::RuntimeDbWeight =
689					<Runtime as frame_system::Config>::DbWeight::get();
690				assert_eq!(w2, db.reads(1), "second call should be a 1-read no-op");
691			});
692		}
693
694		#[test]
695		fn noop_when_validators_already_set() {
696			let keys = [Sr25519Keyring::Alice];
697			ext_with_aura(&keys).execute_with(|| {
698				pallet_session::Validators::<Runtime>::put(alloc::vec![
699					Sr25519Keyring::Alice.to_account_id(),
700				]);
701				let w = EnableAuthorityDiscovery::on_runtime_upgrade();
702				let db: frame_support::weights::RuntimeDbWeight =
703					<Runtime as frame_system::Config>::DbWeight::get();
704				assert_eq!(w, db.reads(1));
705				assert!(pallet_authority_discovery::Keys::<Runtime>::get().is_empty());
706			});
707		}
708	}
709}
710
711decl_runtime_apis! {
712	pub trait GetLastTimestamp {
713		/// Returns the last timestamp of a runtime.
714		fn get_last_timestamp() -> u64;
715	}
716}
717
718impl_runtime_apis! {
719	impl sp_api::Core<Block> for Runtime {
720		fn version() -> RuntimeVersion {
721			VERSION
722		}
723
724		fn execute_block(block: <Block as BlockT>::LazyBlock) {
725			Executive::execute_block(block)
726		}
727
728		fn initialize_block(header: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
729			Executive::initialize_block(header)
730		}
731	}
732
733
734	impl cumulus_primitives_aura::AuraUnincludedSegmentApi<Block> for Runtime {
735		fn can_build_upon(
736			included_hash: <Block as BlockT>::Hash,
737			slot: cumulus_primitives_aura::Slot,
738		) -> bool {
739			ConsensusHook::can_build_upon(included_hash, slot)
740		}
741	}
742
743	impl cumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
744		fn relay_parent_offset() -> u32 {
745			relay_parent_offset()
746		}
747
748		fn max_claim_queue_offset() -> u8 {
749			cumulus_pallet_parachain_system::Pallet::<Runtime>::max_claim_queue_offset()
750		}
751	}
752
753	impl cumulus_primitives_core::SchedulingV3EnabledApi<Block> for Runtime {
754		fn scheduling_v3_enabled() -> bool {
755			<Runtime as cumulus_pallet_parachain_system::Config>::SchedulingSignatureVerifier::V3_SCHEDULING_ENABLED
756		}
757	}
758
759	impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
760		fn slot_duration() -> sp_consensus_aura::SlotDuration {
761			sp_consensus_aura::SlotDuration::from_millis(slot_duration())
762		}
763
764		fn authorities() -> Vec<AuraId> {
765			pallet_aura::Authorities::<Runtime>::get().into_inner()
766		}
767	}
768
769	impl sp_api::Metadata<Block> for Runtime {
770		fn metadata() -> OpaqueMetadata {
771			OpaqueMetadata::new(Runtime::metadata().into())
772		}
773
774		fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
775			Runtime::metadata_at_version(version)
776		}
777
778		fn metadata_versions() -> Vec<u32> {
779			Runtime::metadata_versions()
780		}
781	}
782
783	impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Nonce> for Runtime {
784		fn account_nonce(account: AccountId) -> Nonce {
785			System::account_nonce(account)
786		}
787	}
788
789	impl sp_block_builder::BlockBuilder<Block> for Runtime {
790		fn apply_extrinsic(
791			extrinsic: <Block as BlockT>::Extrinsic,
792		) -> ApplyExtrinsicResult {
793			Executive::apply_extrinsic(extrinsic)
794		}
795
796		fn finalize_block() -> <Block as BlockT>::Header {
797			Executive::finalize_block()
798		}
799
800		fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
801			data.create_extrinsics()
802		}
803
804		fn check_inherents(block: <Block as BlockT>::LazyBlock, data: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult {
805			data.check_extrinsics(&block)
806		}
807
808	}
809
810	impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
811		fn validate_transaction(
812			source: TransactionSource,
813			tx: <Block as BlockT>::Extrinsic,
814			block_hash: <Block as BlockT>::Hash,
815		) -> TransactionValidity {
816			Executive::validate_transaction(source, tx, block_hash)
817		}
818	}
819
820	impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
821		fn offchain_worker(header: &<Block as BlockT>::Header) {
822			Executive::offchain_worker(header)
823		}
824	}
825
826	impl sp_session::SessionKeys<Block> for Runtime {
827		fn decode_session_keys(
828			encoded: Vec<u8>,
829		) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
830			SessionKeys::decode_into_raw_public_keys(&encoded)
831		}
832
833		fn generate_session_keys(owner: Vec<u8>, seed: Option<Vec<u8>>) -> sp_session::OpaqueGeneratedSessionKeys {
834			SessionKeys::generate(&owner, seed).into()
835		}
836	}
837
838	impl crate::GetLastTimestamp<Block> for Runtime {
839		fn get_last_timestamp() -> u64 {
840			Now::<Runtime>::get()
841		}
842	}
843
844	impl cumulus_primitives_core::CollectCollationInfo<Block> for Runtime {
845		fn collect_collation_info(header: &<Block as BlockT>::Header) -> cumulus_primitives_core::CollationInfo {
846			ParachainSystem::collect_collation_info(header)
847		}
848	}
849
850	impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
851		fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
852			build_state::<RuntimeGenesisConfig>(config)
853		}
854
855		fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
856			get_preset::<RuntimeGenesisConfig>(id, genesis_config_presets::get_preset)
857		}
858
859		fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
860			genesis_config_presets::preset_names()
861		}
862	}
863
864	impl cumulus_primitives_core::GetParachainInfo<Block> for Runtime {
865		fn parachain_id() -> ParaId {
866			ParachainInfo::parachain_id()
867		}
868	}
869
870	impl cumulus_primitives_core::TargetBlockRate<Block> for Runtime {
871		fn target_block_rate() -> u32 {
872			block_processing_velocity()
873		}
874	}
875
876	impl cumulus_primitives_core::KeyToIncludeInRelayProof<Block> for Runtime {
877		fn keys_to_prove() -> cumulus_primitives_core::RelayProofRequest {
878			use cumulus_primitives_core::RelayStorageKey;
879			RelayProofRequest {
880				keys: vec![
881					// Request a key to verify its inclusion in the proof.
882					RelayStorageKey::Top(test_pallet::relay_alice_account_key()),
883				],
884			}
885		}
886	}
887
888	impl sp_authority_discovery::AuthorityDiscoveryApi<Block> for Runtime {
889		// Return the current authority set in authoring (session/validator-index) order,
890		fn authorities() -> Vec<AuthorityDiscoveryId> {
891			#[cfg(feature = "with-authority-discovery")]
892			{ AuthorityDiscovery::current_authorities().to_vec() }
893			#[cfg(not(feature = "with-authority-discovery"))]
894			{ Vec::new() }
895		}
896	}
897}
898
899cumulus_pallet_parachain_system::register_validate_block! {
900	Runtime = Runtime,
901	BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
902}