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 DoneSlashHandler = ();
283}
284
285impl pallet_transaction_payment::Config for Runtime {
286	type RuntimeEvent = RuntimeEvent;
287	type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter<Balances, ()>;
288	type WeightToFee = IdentityFee<Balance>;
289	type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
290	type FeeMultiplierUpdate = ();
291	type OperationalFeeMultiplier = ConstU8<5>;
292	type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight<Runtime>;
293}
294
295impl pallet_sudo::Config for Runtime {
296	type RuntimeCall = RuntimeCall;
297	type RuntimeEvent = RuntimeEvent;
298	type WeightInfo = pallet_sudo::weights::SubstrateWeight<Runtime>;
299}
300
301impl pallet_utility::Config for Runtime {
302	type RuntimeCall = RuntimeCall;
303	type RuntimeEvent = RuntimeEvent;
304	type PalletsOrigin = OriginCaller;
305	type WeightInfo = pallet_utility::weights::SubstrateWeight<Runtime>;
306}
307
308impl pallet_glutton::Config for Runtime {
309	type RuntimeEvent = RuntimeEvent;
310	type AdminOrigin = EnsureRoot<AccountId>;
311	type WeightInfo = pallet_glutton::weights::SubstrateWeight<Runtime>;
312}
313
314/// Scheduling-info verifier used by `cumulus-test-runtime`.
315///
316/// Accepts any signature; `V3_SCHEDULING_ENABLED` is gated on the `v3-descriptor` cargo
317/// feature so the test runtime can flip V3 scheduling on without needing a runtime upgrade
318/// per build.
319pub struct NoVerification;
320
321impl VerifySchedulingSignature for NoVerification {
322	const V3_SCHEDULING_ENABLED: bool = SCHEDULING_V3_ENABLED;
323
324	fn verify(
325		_signed_info: &cumulus_primitives_core::SignedSchedulingInfo,
326		_relay_slot: cumulus_primitives_core::relay_chain::Slot,
327	) -> bool {
328		true
329	}
330}
331
332type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook<
333	Runtime,
334	RELAY_CHAIN_SLOT_DURATION_MILLIS,
335	{ block_processing_velocity() },
336	{ unincluded_segment_capacity() },
337>;
338impl cumulus_pallet_parachain_system::Config for Runtime {
339	type WeightInfo = ();
340	type SelfParaId = parachain_info::Pallet<Runtime>;
341	type RuntimeEvent = RuntimeEvent;
342	type OnSystemEvent = TestPallet;
343	type OutboundXcmpMessageSource = TestPallet;
344	// Ignore all DMP messages by enqueueing them into `()`:
345	type DmpQueue = frame_support::traits::EnqueueWithOrigin<(), sp_core::ConstU8<0>>;
346	type ReservedDmpWeight = ();
347	type XcmpMessageHandler = ();
348	type ReservedXcmpWeight = ();
349	type CheckAssociatedRelayNumber =
350		cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
351	type ConsensusHook = ConsensusHook;
352	type RelayParentOffset = ConstU32<{ relay_parent_offset() }>;
353	type SchedulingSignatureVerifier = NoVerification;
354}
355
356impl parachain_info::Config for Runtime {}
357
358impl pallet_aura::Config for Runtime {
359	type AuthorityId = AuraId;
360	type DisabledValidators = ();
361	type MaxAuthorities = ConstU32<32>;
362	type AllowMultipleBlocksPerSlot = ConstBool<{ !cfg!(feature = "sync-backing") }>;
363	type SlotDuration = ConstU64<{ slot_duration() }>;
364}
365
366impl test_pallet::Config for Runtime {}
367
368parameter_types! {
369	pub const Period: u32 = 10;
370}
371
372#[cfg(feature = "with-authority-discovery")]
373impl pallet_session::Config for Runtime {
374	type RuntimeEvent = RuntimeEvent;
375	type ValidatorId = AccountId;
376	type ValidatorIdOf = sp_runtime::traits::ConvertInto;
377	type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
378	type NextSessionRotation = pallet_session::PeriodicSessions<Period, Offset>;
379	type SessionManager = ();
380	type SessionHandler = <SessionKeys as sp_runtime::traits::OpaqueKeys>::KeyTypeIdProviders;
381	type Keys = SessionKeys;
382	type DisablingStrategy = ();
383	type WeightInfo = ();
384	type Currency = Balances;
385	type KeyDeposit = ();
386}
387
388#[cfg(feature = "with-authority-discovery")]
389impl pallet_authority_discovery::Config for Runtime {
390	type MaxAuthorities = ConstU32<32>;
391}
392
393construct_runtime! {
394	pub enum Runtime
395	{
396		System: frame_system,
397		ParachainSystem: cumulus_pallet_parachain_system,
398		Timestamp: pallet_timestamp,
399		ParachainInfo: parachain_info,
400		Balances: pallet_balances,
401		Sudo: pallet_sudo,
402		Utility: pallet_utility,
403		TransactionPayment: pallet_transaction_payment,
404		TestPallet: test_pallet,
405		Glutton: pallet_glutton,
406		Aura: pallet_aura,
407		// Session must come BEFORE AuraExt so its on_genesis_session populates
408		// pallet_aura::Authorities before AuraExt's genesis_build snapshots it.
409		#[cfg(feature = "with-authority-discovery")]
410		Session: pallet_session,
411		#[cfg(feature = "with-authority-discovery")]
412		AuthorityDiscovery: pallet_authority_discovery,
413		AuraExt: cumulus_pallet_aura_ext,
414		WeightReclaim: cumulus_pallet_weight_reclaim,
415	}
416}
417
418/// Index of a transaction in the chain.
419pub type Nonce = u32;
420/// A hash of some data used by the chain.
421pub type Hash = sp_core::H256;
422/// Balance of an account.
423pub type Balance = u128;
424/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
425pub type Signature = MultiSignature;
426/// An index to a block.
427pub type BlockNumber = u32;
428/// Some way of identifying an account on the chain. We intentionally make it equivalent
429/// to the public key of our transaction signing scheme.
430pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
431/// Opaque block type.
432pub type NodeBlock = generic::Block<Header, sp_runtime::OpaqueExtrinsic>;
433
434/// The address format for describing accounts.
435pub type Address = MultiAddress<AccountId, ()>;
436/// Block header type as expected by this runtime.
437pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
438/// Block type as expected by this runtime.
439pub type Block = generic::Block<Header, UncheckedExtrinsic>;
440/// A Block signed with a Justification
441pub type SignedBlock = generic::SignedBlock<Block>;
442/// BlockId type as expected by this runtime.
443pub type BlockId = generic::BlockId<Block>;
444/// The extension to the basic transaction logic.
445pub type TxExtension = cumulus_pallet_parachain_system::block_weight::DynamicMaxBlockWeight<
446	Runtime,
447	cumulus_pallet_weight_reclaim::StorageWeightReclaim<
448		Runtime,
449		(
450			frame_system::AuthorizeCall<Runtime>,
451			frame_system::CheckNonZeroSender<Runtime>,
452			frame_system::CheckSpecVersion<Runtime>,
453			frame_system::CheckGenesis<Runtime>,
454			frame_system::CheckEra<Runtime>,
455			frame_system::CheckNonce<Runtime>,
456			frame_system::CheckWeight<Runtime>,
457			pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
458			test_pallet::TestTransactionExtension<Runtime>,
459		),
460	>,
461	ConstU32<{ block_processing_velocity() }>,
462>;
463
464/// Unchecked extrinsic type as expected by this runtime.
465pub type UncheckedExtrinsic =
466	generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, TxExtension>;
467/// Executive: handles dispatch to the various modules.
468pub type Executive = frame_executive::Executive<
469	Runtime,
470	Block,
471	frame_system::ChainContext<Runtime>,
472	Runtime,
473	AllPalletsWithSystem,
474>;
475
476/// The payload being signed in transactions.
477pub type SignedPayload = generic::SignedPayload<RuntimeCall, TxExtension>;
478
479/// Migration to verify that runtime upgrade hooks are working correctly.
480///
481/// This checks that the test_pallet runtime upgrade key was set in genesis.
482pub struct VerifyRuntimeUpgrade;
483
484impl OnRuntimeUpgrade for VerifyRuntimeUpgrade {
485	fn on_runtime_upgrade() -> Weight {
486		assert_eq!(
487			sp_io::storage::get(test_pallet::TEST_RUNTIME_UPGRADE_KEY),
488			Some(vec![1, 2, 3, 4].into())
489		);
490		Weight::from_parts(1, 0)
491	}
492}
493
494/// Single-block migrations for the test runtime.
495///
496/// These migrations execute immediately and entirely at the beginning of the block following
497/// a runtime upgrade. They must be lightweight enough to complete within a single block.
498#[cfg(feature = "with-authority-discovery")]
499pub type SingleBlockMigrations = (VerifyRuntimeUpgrade, migrations::EnableAuthorityDiscovery);
500#[cfg(not(feature = "with-authority-discovery"))]
501pub type SingleBlockMigrations = (VerifyRuntimeUpgrade,);
502
503/// One-shot migration that seeds `pallet_session` from `pallet_aura::Authorities` when a
504/// default (no-AD) chain upgrades to the `with-authority-discovery` variant.
505///
506/// Idempotent: only runs when `pallet_session::Validators` is empty, which is the case
507/// on a chain that never had `pallet_session` in its runtime.
508#[cfg(feature = "with-authority-discovery")]
509pub mod migrations {
510	use super::*;
511	use sp_core::crypto::key_types;
512
513	pub struct EnableAuthorityDiscovery;
514
515	impl OnRuntimeUpgrade for EnableAuthorityDiscovery {
516		fn on_runtime_upgrade() -> Weight {
517			let db: frame_support::weights::RuntimeDbWeight =
518				<Runtime as frame_system::Config>::DbWeight::get();
519
520			// Idempotent guard: skip if Validators is already populated.
521			if !pallet_session::Validators::<Runtime>::get().is_empty() {
522				return db.reads(1);
523			}
524
525			let aura_authorities = pallet_aura::Authorities::<Runtime>::get();
526			let n = aura_authorities.len() as u64;
527
528			let mut validators: Vec<AccountId> = Vec::with_capacity(aura_authorities.len());
529			let mut queued_keys: Vec<(AccountId, SessionKeys)> =
530				Vec::with_capacity(aura_authorities.len());
531
532			for aura_pub in aura_authorities.iter() {
533				// `AuraId` is app-crypto over `sr25519::Public`; `.into()` gives the inner.
534				let inner: sp_core::sr25519::Public = aura_pub.clone().into();
535				let raw: [u8; 32] = inner.0;
536				let account: AccountId = sp_core::sr25519::Public::from_raw(raw).into();
537				let aura_key = AuraId::from(sp_core::sr25519::Public::from_raw(raw));
538				let audi_key = AuthorityDiscoveryId::from(sp_core::sr25519::Public::from_raw(raw));
539				let session_keys = SessionKeys { aura: aura_key, authority_discovery: audi_key };
540
541				// Populate NextKeys and KeyOwner (mirrors pallet_session genesis logic).
542				pallet_session::NextKeys::<Runtime>::insert(&account, &session_keys);
543				// KeyOwner maps (KeyTypeId, key_bytes: Vec<u8>) → ValidatorId.
544				// We use <[u8]>::to_vec() to get an owned Vec<u8> that EncodeLike<Vec<u8>>.
545				let aura_bytes: alloc::vec::Vec<u8> =
546					<AuraId as sp_runtime::RuntimeAppPublic>::to_raw_vec(&session_keys.aura);
547				let audi_bytes: alloc::vec::Vec<u8> =
548					<AuthorityDiscoveryId as sp_runtime::RuntimeAppPublic>::to_raw_vec(
549						&session_keys.authority_discovery,
550					);
551				pallet_session::KeyOwner::<Runtime>::insert(
552					(key_types::AURA, aura_bytes),
553					&account,
554				);
555				pallet_session::KeyOwner::<Runtime>::insert(
556					(key_types::AUTHORITY_DISCOVERY, audi_bytes),
557					&account,
558				);
559
560				// Mirror `pallet_session::do_set_keys`: increment the account's consumer
561				// count so a future `purge_keys` decrements it correctly. Zombienet-injected
562				// aura keys without endowment are skipped — they have no consumer to track.
563				if frame_system::Pallet::<Runtime>::providers(&account) > 0 {
564					let inc_ok = frame_system::Pallet::<Runtime>::inc_consumers(&account).is_ok();
565					debug_assert!(inc_ok, "inc_consumers failed despite providers > 0");
566				}
567
568				validators.push(account.clone());
569				queued_keys.push((account, session_keys));
570			}
571
572			// Write Validators and QueuedKeys so the session pallet has a coherent state.
573			pallet_session::Validators::<Runtime>::put(&validators);
574			pallet_session::QueuedKeys::<Runtime>::put(&queued_keys);
575
576			// YOLO so these keys are not empty until next session.
577			let ad_authorities: Vec<AuthorityDiscoveryId> = aura_authorities
578				.iter()
579				.map(|aura_pub| {
580					let inner: sp_core::sr25519::Public = aura_pub.clone().into();
581					AuthorityDiscoveryId::from(sp_core::sr25519::Public::from_raw(inner.0))
582				})
583				.collect();
584			let bounded = frame_support::WeakBoundedVec::<_, _>::force_from(
585				ad_authorities,
586				Some("EnableAuthorityDiscovery migration: authority count exceeds MaxAuthorities"),
587			);
588			pallet_authority_discovery::Keys::<Runtime>::put(bounded);
589
590			Self::assert_post_upgrade_invariants();
591
592			let reads = n.saturating_add(2);
593			let writes = n.saturating_mul(4).saturating_add(3);
594			db.reads(reads).saturating_add(db.writes(writes))
595		}
596	}
597
598	impl EnableAuthorityDiscovery {
599		fn assert_post_upgrade_invariants() {
600			let aura_count = pallet_aura::Authorities::<Runtime>::get().len();
601			let validators = pallet_session::Validators::<Runtime>::get();
602			let queued = pallet_session::QueuedKeys::<Runtime>::get();
603			let ad_keys = pallet_authority_discovery::Keys::<Runtime>::get();
604
605			assert!(!validators.is_empty(), "Validators empty after migration");
606			assert_eq!(validators.len(), aura_count, "Validators ≠ aura Authorities");
607			assert_eq!(queued.len(), aura_count, "QueuedKeys ≠ aura Authorities");
608			assert_eq!(ad_keys.len(), aura_count, "AuthorityDiscovery::Keys ≠ aura Authorities");
609			assert_eq!(
610				pallet_session::NextKeys::<Runtime>::iter().count(),
611				aura_count,
612				"NextKeys entry count ≠ aura Authorities",
613			);
614			assert_eq!(
615				pallet_session::KeyOwner::<Runtime>::iter().count(),
616				2 * aura_count,
617				"KeyOwner count ≠ 2× aura Authorities (aura + audi)",
618			);
619			// Each provisioned validator account had its consumer count bumped by
620			// `inc_consumers`, mirroring `pallet_session::do_set_keys` semantics.
621			// Un-provisioned aura authorities (e.g. extra zombienet-generated collator keys
622			// that aren't in the endowed-accounts list) are skipped: `inc_consumers`
623			// returned `Err` for them at migration time, and they have no consumer to bump.
624			for account in &validators {
625				if frame_system::Pallet::<Runtime>::providers(account) > 0 {
626					assert!(
627						frame_system::Pallet::<Runtime>::consumers(account) >= 1,
628						"provisioned validator {account:?} has 0 consumers; \
629						 inc_consumers didn't fire",
630					);
631				}
632			}
633		}
634	}
635
636	#[cfg(test)]
637	mod tests {
638		use super::*;
639		use frame_support::traits::OnRuntimeUpgrade;
640		use sp_keyring::Sr25519Keyring;
641
642		fn ext_with_aura(keys: &[Sr25519Keyring]) -> sp_io::TestExternalities {
643			let mut ext = sp_io::TestExternalities::new_empty();
644			ext.execute_with(|| {
645				let aura_keys: alloc::vec::Vec<AuraId> = keys
646					.iter()
647					.map(|k| AuraId::from(sp_core::sr25519::Public::from_raw(k.public().0)))
648					.collect();
649				let bounded = frame_support::BoundedVec::<_, _>::try_from(aura_keys).expect("fits");
650				pallet_aura::Authorities::<Runtime>::put(bounded);
651				// Provision providers so the migration's `inc_consumers` call can succeed —
652				// production parachains rely on every authority account being funded.
653				for k in keys {
654					let account: AccountId = k.to_account_id();
655					frame_system::Pallet::<Runtime>::inc_providers(&account);
656				}
657			});
658			ext
659		}
660
661		fn expected_weight(n: u64) -> Weight {
662			let db: frame_support::weights::RuntimeDbWeight =
663				<Runtime as frame_system::Config>::DbWeight::get();
664			let reads = n.saturating_add(2);
665			let writes = n.saturating_mul(4).saturating_add(3);
666			db.reads(reads).saturating_add(db.writes(writes))
667		}
668
669		#[test]
670		fn populates_session_state() {
671			// Invariants are asserted in `on_runtime_upgrade`.
672			let keys = [Sr25519Keyring::Alice, Sr25519Keyring::Bob, Sr25519Keyring::Charlie];
673			ext_with_aura(&keys).execute_with(|| {
674				let w = EnableAuthorityDiscovery::on_runtime_upgrade();
675				assert_eq!(w, expected_weight(keys.len() as u64));
676			});
677		}
678
679		#[test]
680		fn is_idempotent() {
681			let keys = [Sr25519Keyring::Alice, Sr25519Keyring::Bob];
682			ext_with_aura(&keys).execute_with(|| {
683				EnableAuthorityDiscovery::on_runtime_upgrade();
684				let w2 = EnableAuthorityDiscovery::on_runtime_upgrade();
685
686				let db: frame_support::weights::RuntimeDbWeight =
687					<Runtime as frame_system::Config>::DbWeight::get();
688				assert_eq!(w2, db.reads(1), "second call should be a 1-read no-op");
689			});
690		}
691
692		#[test]
693		fn noop_when_validators_already_set() {
694			let keys = [Sr25519Keyring::Alice];
695			ext_with_aura(&keys).execute_with(|| {
696				pallet_session::Validators::<Runtime>::put(alloc::vec![
697					Sr25519Keyring::Alice.to_account_id(),
698				]);
699				let w = EnableAuthorityDiscovery::on_runtime_upgrade();
700				let db: frame_support::weights::RuntimeDbWeight =
701					<Runtime as frame_system::Config>::DbWeight::get();
702				assert_eq!(w, db.reads(1));
703				assert!(pallet_authority_discovery::Keys::<Runtime>::get().is_empty());
704			});
705		}
706	}
707}
708
709decl_runtime_apis! {
710	pub trait GetLastTimestamp {
711		/// Returns the last timestamp of a runtime.
712		fn get_last_timestamp() -> u64;
713	}
714}
715
716impl_runtime_apis! {
717	impl sp_api::Core<Block> for Runtime {
718		fn version() -> RuntimeVersion {
719			VERSION
720		}
721
722		fn execute_block(block: <Block as BlockT>::LazyBlock) {
723			Executive::execute_block(block)
724		}
725
726		fn initialize_block(header: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
727			Executive::initialize_block(header)
728		}
729	}
730
731
732	impl cumulus_primitives_aura::AuraUnincludedSegmentApi<Block> for Runtime {
733		fn can_build_upon(
734			included_hash: <Block as BlockT>::Hash,
735			slot: cumulus_primitives_aura::Slot,
736		) -> bool {
737			ConsensusHook::can_build_upon(included_hash, slot)
738		}
739	}
740
741	impl cumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
742		fn relay_parent_offset() -> u32 {
743			relay_parent_offset()
744		}
745
746		fn max_claim_queue_offset() -> u8 {
747			cumulus_pallet_parachain_system::Pallet::<Runtime>::max_claim_queue_offset()
748		}
749	}
750
751	impl cumulus_primitives_core::SchedulingV3EnabledApi<Block> for Runtime {
752		fn scheduling_v3_enabled() -> bool {
753			<Runtime as cumulus_pallet_parachain_system::Config>::SchedulingSignatureVerifier::V3_SCHEDULING_ENABLED
754		}
755	}
756
757	impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
758		fn slot_duration() -> sp_consensus_aura::SlotDuration {
759			sp_consensus_aura::SlotDuration::from_millis(slot_duration())
760		}
761
762		fn authorities() -> Vec<AuraId> {
763			pallet_aura::Authorities::<Runtime>::get().into_inner()
764		}
765	}
766
767	impl sp_api::Metadata<Block> for Runtime {
768		fn metadata() -> OpaqueMetadata {
769			OpaqueMetadata::new(Runtime::metadata().into())
770		}
771
772		fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
773			Runtime::metadata_at_version(version)
774		}
775
776		fn metadata_versions() -> Vec<u32> {
777			Runtime::metadata_versions()
778		}
779	}
780
781	impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Nonce> for Runtime {
782		fn account_nonce(account: AccountId) -> Nonce {
783			System::account_nonce(account)
784		}
785	}
786
787	impl sp_block_builder::BlockBuilder<Block> for Runtime {
788		fn apply_extrinsic(
789			extrinsic: <Block as BlockT>::Extrinsic,
790		) -> ApplyExtrinsicResult {
791			Executive::apply_extrinsic(extrinsic)
792		}
793
794		fn finalize_block() -> <Block as BlockT>::Header {
795			Executive::finalize_block()
796		}
797
798		fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
799			data.create_extrinsics()
800		}
801
802		fn check_inherents(block: <Block as BlockT>::LazyBlock, data: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult {
803			data.check_extrinsics(&block)
804		}
805
806	}
807
808	impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
809		fn validate_transaction(
810			source: TransactionSource,
811			tx: <Block as BlockT>::Extrinsic,
812			block_hash: <Block as BlockT>::Hash,
813		) -> TransactionValidity {
814			Executive::validate_transaction(source, tx, block_hash)
815		}
816	}
817
818	impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
819		fn offchain_worker(header: &<Block as BlockT>::Header) {
820			Executive::offchain_worker(header)
821		}
822	}
823
824	impl sp_session::SessionKeys<Block> for Runtime {
825		fn decode_session_keys(
826			encoded: Vec<u8>,
827		) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
828			SessionKeys::decode_into_raw_public_keys(&encoded)
829		}
830
831		fn generate_session_keys(owner: Vec<u8>, seed: Option<Vec<u8>>) -> sp_session::OpaqueGeneratedSessionKeys {
832			SessionKeys::generate(&owner, seed).into()
833		}
834	}
835
836	impl crate::GetLastTimestamp<Block> for Runtime {
837		fn get_last_timestamp() -> u64 {
838			Now::<Runtime>::get()
839		}
840	}
841
842	impl cumulus_primitives_core::CollectCollationInfo<Block> for Runtime {
843		fn collect_collation_info(header: &<Block as BlockT>::Header) -> cumulus_primitives_core::CollationInfo {
844			ParachainSystem::collect_collation_info(header)
845		}
846	}
847
848	impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
849		fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
850			build_state::<RuntimeGenesisConfig>(config)
851		}
852
853		fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
854			get_preset::<RuntimeGenesisConfig>(id, genesis_config_presets::get_preset)
855		}
856
857		fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
858			genesis_config_presets::preset_names()
859		}
860	}
861
862	impl cumulus_primitives_core::GetParachainInfo<Block> for Runtime {
863		fn parachain_id() -> ParaId {
864			ParachainInfo::parachain_id()
865		}
866	}
867
868	impl cumulus_primitives_core::TargetBlockRate<Block> for Runtime {
869		fn target_block_rate() -> u32 {
870			block_processing_velocity()
871		}
872	}
873
874	impl cumulus_primitives_core::KeyToIncludeInRelayProof<Block> for Runtime {
875		fn keys_to_prove() -> cumulus_primitives_core::RelayProofRequest {
876			use cumulus_primitives_core::RelayStorageKey;
877			RelayProofRequest {
878				keys: vec![
879					// Request a key to verify its inclusion in the proof.
880					RelayStorageKey::Top(test_pallet::relay_alice_account_key()),
881				],
882			}
883		}
884	}
885
886	impl sp_authority_discovery::AuthorityDiscoveryApi<Block> for Runtime {
887		// Return the current authority set in authoring (session/validator-index) order,
888		fn authorities() -> Vec<AuthorityDiscoveryId> {
889			#[cfg(feature = "with-authority-discovery")]
890			{ AuthorityDiscovery::current_authorities().to_vec() }
891			#[cfg(not(feature = "with-authority-discovery"))]
892			{ Vec::new() }
893		}
894	}
895}
896
897cumulus_pallet_parachain_system::register_validate_block! {
898	Runtime = Runtime,
899	BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
900}