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