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