partner_chains_demo_runtime/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
3#![recursion_limit = "256"]
4#![allow(deprecated)]
5
6#[cfg(feature = "runtime-benchmarks")]
7#[macro_use]
8extern crate frame_benchmarking;
9
10extern crate alloc;
11
12use alloc::collections::BTreeMap;
13use alloc::string::String;
14use authority_selection_inherents::CommitteeMember;
15use authority_selection_inherents::authority_selection_inputs::AuthoritySelectionInputs;
16use authority_selection_inherents::filter_invalid_candidates::{
17	PermissionedCandidateDataError, RegistrationDataError, StakeError,
18	validate_permissioned_candidate_data,
19};
20use authority_selection_inherents::select_authorities::select_authorities;
21use frame_support::genesis_builder_helper::{build_state, get_preset};
22use frame_support::inherent::ProvideInherent;
23use frame_support::weights::constants::RocksDbWeight as RuntimeDbWeight;
24use frame_support::{
25	BoundedVec, construct_runtime, parameter_types,
26	traits::{ConstBool, ConstU8, ConstU16, ConstU32, ConstU64, ConstU128},
27	weights::{IdentityFee, constants::WEIGHT_REF_TIME_PER_SECOND},
28};
29use frame_system::EnsureRoot;
30use opaque::SessionKeys;
31use pallet_block_producer_metadata;
32use pallet_grandpa::AuthorityId as GrandpaId;
33use pallet_session_validator_management::session_manager::ValidatorManagementSessionManager;
34use pallet_transaction_payment::{ConstFeeMultiplier, FungibleAdapter, Multiplier};
35use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
36use scale_info::TypeInfo;
37use serde::{Deserialize, Serialize};
38use sidechain_domain::byte_string::{BoundedString, ByteString, SizedByteString};
39use sidechain_domain::{
40	CrossChainPublicKey, DelegatorKey, MainchainKeyHash, PermissionedCandidateData,
41	RegistrationData, ScEpochNumber, ScSlotNumber, StakeDelegation, StakePoolPublicKey, UtxoId,
42};
43use sidechain_slots::Slot;
44use sp_api::impl_runtime_apis;
45use sp_block_participation::AsCardanoSPO;
46use sp_consensus_aura::sr25519::AuthorityId as AuraId;
47#[cfg(feature = "runtime-benchmarks")]
48use sp_core::ByteArray;
49use sp_core::{OpaqueMetadata, crypto::KeyTypeId};
50use sp_governed_map::MainChainScriptsV1;
51use sp_inherents::InherentIdentifier;
52use sp_runtime::{
53	ApplyExtrinsicResult, MultiSignature, Perbill, generic, impl_opaque_keys,
54	traits::{
55		AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, NumberFor, One, OpaqueKeys,
56		Verify,
57	},
58	transaction_validity::{TransactionSource, TransactionValidity},
59};
60use sp_sidechain::SidechainStatus;
61use sp_std::prelude::*;
62use sp_version::RuntimeVersion;
63use sp_weights::Weight;
64
65// Make the WASM binary available.
66#[cfg(feature = "std")]
67include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
68
69pub mod genesis_config_presets;
70
71#[cfg(test)]
72mod mock;
73
74#[cfg(test)]
75mod header_tests;
76
77mod test_helper_pallet;
78
79/// An index to a block.
80pub type BlockNumber = u32;
81
82/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
83pub type Signature = MultiSignature;
84
85/// Some way of identifying an account on the chain. We intentionally make it equivalent
86/// to the public key of our transaction signing scheme.
87pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
88
89/// Balance of an account.
90pub type Balance = u128;
91
92/// Index of a transaction in the chain.
93pub type Nonce = u32;
94
95/// A hash of some data used by the chain.
96pub type Hash = sp_core::H256;
97
98/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
99/// the specifics of the runtime. They can then be made to be agnostic over specific formats
100/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
101/// to even the core data structures.
102pub mod opaque {
103	use super::*;
104	use parity_scale_codec::MaxEncodedLen;
105	use sp_core::{ed25519, sr25519};
106	pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
107
108	/// Opaque block header type.
109	pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
110	/// Opaque block type.
111	pub type Block = generic::Block<Header, UncheckedExtrinsic>;
112	/// Opaque block identifier type.
113	pub type BlockId = generic::BlockId<Block>;
114
115	pub const CROSS_CHAIN: KeyTypeId = KeyTypeId(*b"crch");
116	pub struct CrossChainRuntimeAppPublic;
117
118	pub mod cross_chain_app {
119		use super::CROSS_CHAIN;
120		use parity_scale_codec::MaxEncodedLen;
121		use sidechain_domain::SidechainPublicKey;
122		use sp_core::crypto::AccountId32;
123		use sp_runtime::MultiSigner;
124		use sp_runtime::app_crypto::{app_crypto, ecdsa};
125		use sp_runtime::traits::IdentifyAccount;
126		use sp_std::vec::Vec;
127
128		app_crypto!(ecdsa, CROSS_CHAIN);
129		impl MaxEncodedLen for Signature {
130			fn max_encoded_len() -> usize {
131				ecdsa::Signature::max_encoded_len()
132			}
133		}
134
135		impl From<Signature> for Vec<u8> {
136			fn from(value: Signature) -> Self {
137				value.into_inner().0.to_vec()
138			}
139		}
140
141		impl From<Public> for AccountId32 {
142			fn from(value: Public) -> Self {
143				MultiSigner::from(ecdsa::Public::from(value)).into_account()
144			}
145		}
146
147		impl From<Public> for Vec<u8> {
148			fn from(value: Public) -> Self {
149				value.into_inner().0.to_vec()
150			}
151		}
152
153		impl TryFrom<SidechainPublicKey> for Public {
154			type Error = SidechainPublicKey;
155			fn try_from(pubkey: SidechainPublicKey) -> Result<Self, Self::Error> {
156				let cross_chain_public_key =
157					Public::try_from(pubkey.0.as_slice()).map_err(|_| pubkey)?;
158				Ok(cross_chain_public_key)
159			}
160		}
161	}
162
163	impl_opaque_keys! {
164		#[derive(MaxEncodedLen, PartialOrd, Ord)]
165		pub struct SessionKeys {
166			pub aura: Aura,
167			pub grandpa: Grandpa,
168		}
169	}
170	impl From<(sr25519::Public, ed25519::Public)> for SessionKeys {
171		fn from((aura, grandpa): (sr25519::Public, ed25519::Public)) -> Self {
172			Self { aura: aura.into(), grandpa: grandpa.into() }
173		}
174	}
175
176	impl_opaque_keys! {
177		pub struct CrossChainKey {
178			pub account: CrossChainPublic,
179		}
180	}
181}
182
183pub type CrossChainPublic = opaque::cross_chain_app::Public;
184
185// To learn more about runtime versioning, see:
186// https://docs.substrate.io/main-docs/build/upgrade#runtime-versioning
187#[sp_version::runtime_version]
188pub const VERSION: RuntimeVersion = RuntimeVersion {
189	spec_name: alloc::borrow::Cow::Borrowed("cardano-sidechain"),
190	impl_name: alloc::borrow::Cow::Borrowed("cardano-sidechain"),
191	authoring_version: 1,
192	// The version of the runtime specification. A full node will not attempt to use its native
193	//   runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`,
194	//   `spec_version`, and `authoring_version` are the same between Wasm and native.
195	// This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
196	//   the compatible custom types.
197	spec_version: 170,
198	impl_version: 1,
199	apis: RUNTIME_API_VERSIONS,
200	transaction_version: 1,
201	system_version: 1,
202};
203
204/// This determines the average expected block time that we are targeting.
205/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`.
206/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked
207/// up by `pallet_aura` to implement `fn slot_duration()`.
208///
209/// Change this to adjust the block time.
210pub const MILLISECS_PER_BLOCK: u64 = 6000;
211
212// NOTE: Currently it is not possible to change the slot duration after the chain has started.
213//       Attempting to do so will brick block production.
214pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
215
216// Time is measured by number of blocks.
217pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
218pub const HOURS: BlockNumber = MINUTES * 60;
219pub const DAYS: BlockNumber = HOURS * 24;
220
221const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
222
223/// We allow for 2 seconds of compute with a 6 second average block time.
224pub const MAXIMUM_BLOCK_WEIGHT: Weight =
225	Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX);
226pub const MAXIMUM_BLOCK_LENGTH: u32 = 5 * 1024 * 1024;
227
228parameter_types! {
229	pub const BlockHashCount: BlockNumber = 2400;
230	pub const Version: RuntimeVersion = VERSION;
231	pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights
232		::with_sensible_defaults(MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO);
233	pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength
234		::max_with_normal_ratio(MAXIMUM_BLOCK_LENGTH, NORMAL_DISPATCH_RATIO);
235	pub const SS58Prefix: u8 = 42;
236}
237
238// Configure FRAME pallets to include in runtime.
239
240impl frame_system::Config for Runtime {
241	/// The basic call filter to use in dispatchable.
242	type BaseCallFilter = frame_support::traits::Everything;
243	/// The block type for the runtime.
244	type Block = Block;
245	/// The type for storing how many extrinsics an account has signed.
246	type Nonce = Nonce;
247	/// Block & extrinsics weights: base values and limits.
248	type BlockWeights = BlockWeights;
249	/// The maximum length of a block (in bytes).
250	type BlockLength = BlockLength;
251	/// The identifier used to distinguish between accounts.
252	type AccountId = AccountId;
253	/// The aggregated dispatch type that is available for extrinsics.
254	type RuntimeCall = RuntimeCall;
255	/// The lookup mechanism to get account ID from whatever is passed in dispatchers.
256	type Lookup = AccountIdLookup<AccountId, ()>;
257	/// The type for hashing blocks and tries.
258	type Hash = Hash;
259	/// The hashing algorithm used.
260	type Hashing = BlakeTwo256;
261	/// The ubiquitous event type.
262	type RuntimeEvent = RuntimeEvent;
263	/// The ubiquitous origin type.
264	type RuntimeOrigin = RuntimeOrigin;
265	/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
266	type BlockHashCount = BlockHashCount;
267	/// The weight of database operations that the runtime can invoke.
268	type DbWeight = RuntimeDbWeight;
269	/// Version of the runtime.
270	type Version = Version;
271	/// Converts a module to the index of the module in `construct_runtime!`.
272	///
273	/// This type is being generated by `construct_runtime!`.
274	type PalletInfo = PalletInfo;
275	/// What to do if a new account is created.
276	type OnNewAccount = ();
277	/// What to do if an account is fully reaped from the system.
278	type OnKilledAccount = ();
279	/// The data to be stored in an account.
280	type AccountData = pallet_balances::AccountData<Balance>;
281	/// Weight information for the extrinsics of this pallet.
282	type SystemWeightInfo = frame_system::weights::SubstrateWeight<Runtime>;
283	// WeightInfo for extensions is present but not accessible in polkadot-stable2412-1, because of that we are using () in our demo runtime
284	type ExtensionsWeightInfo = ();
285	/// This is used as an identifier of the chain. 42 is the generic substrate prefix.
286	type SS58Prefix = SS58Prefix;
287	/// The set code logic, just the default since we're not a parachain.
288	type OnSetCode = ();
289	type MaxConsumers = frame_support::traits::ConstU32<16>;
290	type RuntimeTask = RuntimeTask;
291	type SingleBlockMigrations = ();
292	type MultiBlockMigrator = ();
293	type PreInherents = ();
294	type PostInherents = ();
295	type PostTransactions = ();
296}
297
298impl pallet_native_token_management::Config for Runtime {
299	type RuntimeEvent = RuntimeEvent;
300	type TokenTransferHandler = TestHelperPallet;
301	type MainChainScriptsOrigin = EnsureRoot<Self::AccountId>;
302	type WeightInfo = pallet_native_token_management::weights::SubstrateWeight<Runtime>;
303}
304
305impl pallet_aura::Config for Runtime {
306	type AuthorityId = AuraId;
307	type DisabledValidators = ();
308	type MaxAuthorities = MaxValidators;
309	type AllowMultipleBlocksPerSlot = ConstBool<false>;
310	type SlotDuration = ConstU64<SLOT_DURATION>;
311}
312
313pallet_partner_chains_session::impl_pallet_session_config!(Runtime);
314
315impl pallet_grandpa::Config for Runtime {
316	type RuntimeEvent = RuntimeEvent;
317
318	type WeightInfo = ();
319	type MaxAuthorities = MaxValidators;
320	type MaxNominators = ConstU32<0>;
321	type MaxSetIdSessionEntries = ConstU64<0>;
322
323	type KeyOwnerProof = sp_core::Void;
324	type EquivocationReportSystem = ();
325}
326
327impl pallet_timestamp::Config for Runtime {
328	/// A timestamp: milliseconds since the unix epoch.
329	type Moment = u64;
330	type OnTimestampSet = Aura;
331	type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>;
332	type WeightInfo = pallet_timestamp::weights::SubstrateWeight<Runtime>;
333}
334
335/// Existential deposit.
336pub const EXISTENTIAL_DEPOSIT: u128 = 500;
337
338impl pallet_balances::Config for Runtime {
339	type MaxLocks = ConstU32<50>;
340	type MaxReserves = ();
341	type ReserveIdentifier = [u8; 8];
342	/// The type for recording an account's balance.
343	type Balance = Balance;
344	/// The ubiquitous event type.
345	type RuntimeEvent = RuntimeEvent;
346	type DustRemoval = ();
347	type ExistentialDeposit = ConstU128<EXISTENTIAL_DEPOSIT>;
348	type AccountStore = System;
349	type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>;
350	type FreezeIdentifier = ();
351	type MaxFreezes = ();
352	type RuntimeHoldReason = ();
353	type RuntimeFreezeReason = RuntimeFreezeReason;
354	type DoneSlashHandler = ();
355}
356
357parameter_types! {
358	pub FeeMultiplier: Multiplier = Multiplier::one();
359}
360
361impl pallet_transaction_payment::Config for Runtime {
362	type RuntimeEvent = RuntimeEvent;
363	type OnChargeTransaction = FungibleAdapter<Balances, ()>;
364	type OperationalFeeMultiplier = ConstU8<5>;
365	type WeightToFee = IdentityFee<Balance>;
366	type LengthToFee = IdentityFee<Balance>;
367	type FeeMultiplierUpdate = ConstFeeMultiplier<FeeMultiplier>;
368	type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight<Runtime>;
369}
370
371impl pallet_sudo::Config for Runtime {
372	type RuntimeEvent = RuntimeEvent;
373	type RuntimeCall = RuntimeCall;
374	type WeightInfo = pallet_sudo::weights::SubstrateWeight<Runtime>;
375}
376
377impl pallet_partner_chains_session::Config for Runtime {
378	type RuntimeEvent = RuntimeEvent;
379	type ValidatorId = <Self as frame_system::Config>::AccountId;
380	type ShouldEndSession = ValidatorManagementSessionManager<Runtime>;
381	type NextSessionRotation = ();
382	type SessionManager = ValidatorManagementSessionManager<Runtime>;
383	type SessionHandler = <opaque::SessionKeys as OpaqueKeys>::KeyTypeIdProviders;
384	type Keys = opaque::SessionKeys;
385}
386
387parameter_types! {
388	pub const MaxValidators: u32 = 32;
389}
390
391impl pallet_session_validator_management::Config for Runtime {
392	type RuntimeEvent = RuntimeEvent;
393	type MaxValidators = MaxValidators;
394	type AuthorityId = CrossChainPublic;
395	type AuthorityKeys = SessionKeys;
396	type AuthoritySelectionInputs = AuthoritySelectionInputs;
397	type ScEpochNumber = ScEpochNumber;
398	type WeightInfo = pallet_session_validator_management::weights::SubstrateWeight<Runtime>;
399	type CommitteeMember = CommitteeMember<CrossChainPublic, SessionKeys>;
400	type MainChainScriptsOrigin = EnsureRoot<Self::AccountId>;
401
402	fn select_authorities(
403		input: AuthoritySelectionInputs,
404		sidechain_epoch: ScEpochNumber,
405	) -> Option<BoundedVec<Self::CommitteeMember, Self::MaxValidators>> {
406		Some(BoundedVec::truncate_from(
407			select_authorities(Sidechain::genesis_utxo(), input, sidechain_epoch)?
408				.into_iter()
409				.map(|member| member.into())
410				.collect(),
411		))
412	}
413
414	fn current_epoch_number() -> ScEpochNumber {
415		Sidechain::current_epoch_number()
416	}
417}
418
419parameter_types! {
420	pub const TokenConversionRate: u128 = 1_000_000_000u128;
421	pub const MaxTransactions: u32 = 256u32;
422}
423
424impl pallet_sidechain::Config for Runtime {
425	fn current_slot_number() -> ScSlotNumber {
426		ScSlotNumber(*pallet_aura::CurrentSlot::<Self>::get())
427	}
428	type OnNewEpoch = TestHelperPallet;
429}
430
431pub type BeneficiaryId = sidechain_domain::byte_string::SizedByteString<32>;
432
433#[derive(
434	MaxEncodedLen,
435	Encode,
436	Decode,
437	DecodeWithMemTracking,
438	Clone,
439	TypeInfo,
440	PartialEq,
441	Eq,
442	Debug,
443	Hash,
444	PartialOrd,
445	Ord,
446)]
447pub enum BlockAuthor {
448	Incentivized(CrossChainPublic, StakePoolPublicKey),
449	ProBono(CrossChainPublic),
450}
451impl BlockAuthor {
452	pub fn id(&self) -> &CrossChainPublic {
453		match self {
454			Self::Incentivized(id, _) => id,
455			Self::ProBono(id) => id,
456		}
457	}
458}
459impl From<CommitteeMember<CrossChainPublic, SessionKeys>> for BlockAuthor {
460	fn from(value: CommitteeMember<CrossChainPublic, SessionKeys>) -> Self {
461		match value {
462			CommitteeMember::Permissioned { id, .. } => BlockAuthor::ProBono(id),
463			CommitteeMember::Registered { id, stake_pool_pub_key, .. } => {
464				BlockAuthor::Incentivized(id, stake_pool_pub_key)
465			},
466		}
467	}
468}
469
470impl AsCardanoSPO for BlockAuthor {
471	fn as_cardano_spo(&self) -> Option<MainchainKeyHash> {
472		match self {
473			BlockAuthor::Incentivized(_, key) => Some(key.hash()),
474			BlockAuthor::ProBono(_) => None,
475		}
476	}
477}
478
479pub const MAX_METADATA_URL_LENGTH: u32 = 512;
480
481#[derive(
482	Clone,
483	Debug,
484	MaxEncodedLen,
485	Encode,
486	Decode,
487	DecodeWithMemTracking,
488	Serialize,
489	Deserialize,
490	PartialEq,
491	Eq,
492	TypeInfo,
493)]
494pub struct BlockProducerMetadataType {
495	pub url: BoundedString<ConstU32<MAX_METADATA_URL_LENGTH>>,
496	pub hash: SizedByteString<32>,
497}
498
499#[cfg(feature = "runtime-benchmarks")]
500pub struct PalletBlockProductionLogBenchmarkHelper;
501
502#[cfg(feature = "runtime-benchmarks")]
503impl pallet_block_production_log::benchmarking::BenchmarkHelper<BlockAuthor>
504	for PalletBlockProductionLogBenchmarkHelper
505{
506	fn producer_id() -> BlockAuthor {
507		let id = sp_core::ecdsa::Public::from_slice(&[0u8; 33]).unwrap().into();
508		BlockAuthor::ProBono(id)
509	}
510}
511
512#[cfg(feature = "runtime-benchmarks")]
513pub struct PalletBlockProducerMetadataBenchmarkHelper;
514
515#[cfg(feature = "runtime-benchmarks")]
516impl pallet_block_producer_metadata::benchmarking::BenchmarkHelper<BlockProducerMetadataType>
517	for PalletBlockProducerMetadataBenchmarkHelper
518{
519	fn metadata() -> BlockProducerMetadataType {
520		BlockProducerMetadataType {
521			url: "https://cool.stuff/spo.json".try_into().unwrap(),
522			hash: SizedByteString::from([0; 32]),
523		}
524	}
525	fn cross_chain_pub_key() -> sidechain_domain::CrossChainPublicKey {
526		sidechain_domain::CrossChainPublicKey(
527			hex_literal::hex!("020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1")
528				.to_vec(),
529		)
530	}
531	fn cross_chain_signature() -> sidechain_domain::CrossChainSignature {
532		sidechain_domain::CrossChainSignature(hex_literal::hex!("d1e02e4a5484c3b7202ce6b844577048e7578dc62901cf8f51e6d74bbd3adb091688feacedd8343d0b04a0f5862b2e06148934a75e678e42051fde5431eca33d").to_vec())
533	}
534}
535
536impl pallet_block_production_log::Config for Runtime {
537	type BlockProducerId = BlockAuthor;
538	type WeightInfo = pallet_block_production_log::weights::SubstrateWeight<Runtime>;
539
540	fn current_slot() -> sp_consensus_slots::Slot {
541		let slot: u64 = pallet_aura::CurrentSlot::<Runtime>::get().into();
542		sp_consensus_slots::Slot::from(slot)
543	}
544
545	#[cfg(feature = "runtime-benchmarks")]
546	type BenchmarkHelper = PalletBlockProductionLogBenchmarkHelper;
547}
548
549impl pallet_address_associations::Config for Runtime {
550	type WeightInfo = pallet_address_associations::weights::SubstrateWeight<Runtime>;
551
552	type PartnerChainAddress = AccountId;
553
554	fn genesis_utxo() -> UtxoId {
555		Sidechain::genesis_utxo()
556	}
557
558	type OnNewAssociation = TestHelperPallet;
559}
560
561#[cfg(feature = "runtime-benchmarks")]
562pub struct PalletBlockProducerFeesBenchmarkHelper;
563
564#[cfg(feature = "runtime-benchmarks")]
565impl pallet_block_producer_fees::benchmarking::BenchmarkHelper<AccountId>
566	for PalletBlockProducerFeesBenchmarkHelper
567{
568	fn account_id(i: u8) -> AccountId {
569		sp_core::sr25519::Public::from_raw([i; 32]).into()
570	}
571}
572
573impl pallet_block_producer_fees::Config for Runtime {
574	type WeightInfo = ();
575
576	type HistoricalChangesPerProducer = ConstU16<5>;
577
578	fn current_slot() -> sp_consensus_slots::Slot {
579		let slot: u64 = pallet_aura::CurrentSlot::<Runtime>::get().into();
580		sp_consensus_slots::Slot::from(slot)
581	}
582
583	#[cfg(feature = "runtime-benchmarks")]
584	type BenchmarkHelper = PalletBlockProducerFeesBenchmarkHelper;
585}
586
587impl pallet_block_producer_metadata::Config for Runtime {
588	type WeightInfo = pallet_block_producer_metadata::weights::SubstrateWeight<Runtime>;
589
590	type BlockProducerMetadata = BlockProducerMetadataType;
591
592	fn genesis_utxo() -> UtxoId {
593		Sidechain::genesis_utxo()
594	}
595
596	#[cfg(feature = "runtime-benchmarks")]
597	type BenchmarkHelper = PalletBlockProducerMetadataBenchmarkHelper;
598}
599
600impl pallet_block_participation::Config for Runtime {
601	type WeightInfo = pallet_block_participation::weights::SubstrateWeight<Runtime>;
602	type BlockAuthor = BlockAuthor;
603	type DelegatorId = DelegatorKey;
604
605	fn should_release_data(slot: sidechain_slots::Slot) -> Option<sidechain_slots::Slot> {
606		TestHelperPallet::should_release_participation_data(slot)
607	}
608
609	fn blocks_produced_up_to_slot(slot: Slot) -> impl Iterator<Item = (Slot, BlockAuthor)> {
610		BlockProductionLog::peek_prefix(slot)
611	}
612
613	fn discard_blocks_produced_up_to_slot(slot: Slot) {
614		BlockProductionLog::drop_prefix(&slot)
615	}
616
617	const TARGET_INHERENT_ID: InherentIdentifier = TestHelperPallet::INHERENT_IDENTIFIER;
618}
619
620parameter_types! {
621	pub const MaxChanges: u32 = 16;
622	pub const MaxKeyLength: u32 = 64;
623	pub const MaxValueLength: u32 = 512;
624}
625
626impl pallet_governed_map::Config for Runtime {
627	type MaxChanges = MaxChanges;
628	type MaxKeyLength = MaxKeyLength;
629	type MaxValueLength = MaxValueLength;
630	type WeightInfo = pallet_governed_map::weights::SubstrateWeight<Runtime>;
631
632	type OnGovernedMappingChange = TestHelperPallet;
633	type MainChainScriptsOrigin = EnsureRoot<Self::AccountId>;
634
635	#[cfg(feature = "runtime-benchmarks")]
636	type BenchmarkHelper = ();
637}
638
639impl crate::test_helper_pallet::Config for Runtime {}
640
641// Create the runtime by composing the FRAME pallets that were previously configured.
642construct_runtime!(
643	pub struct Runtime {
644		System: frame_system,
645		Timestamp: pallet_timestamp,
646		Aura: pallet_aura,
647		Grandpa: pallet_grandpa,
648		Balances: pallet_balances,
649		TransactionPayment: pallet_transaction_payment,
650		Sudo: pallet_sudo,
651		// Custom Pallets
652		// Sidechain pallet must come after the Aura pallet, since it gets the slot number from it
653		Sidechain: pallet_sidechain,
654		SessionCommitteeManagement: pallet_session_validator_management,
655		AddressAssociations: pallet_address_associations,
656		BlockProducerFees: pallet_block_producer_fees,
657		BlockProducerMetadata: pallet_block_producer_metadata,
658		BlockProductionLog: pallet_block_production_log,
659		BlockParticipation: pallet_block_participation,
660		// pallet_grandpa reads pallet_session::pallet::CurrentIndex storage.
661		// Only stub implementation of pallet_session should be wired.
662		// Partner Chains session_manager ValidatorManagementSessionManager writes to pallet_session::pallet::CurrentIndex.
663		// ValidatorManagementSessionManager is wired in by pallet_partner_chains_session.
664		PalletSession: pallet_session,
665		// The order matters!! pallet_partner_chains_session needs to come last for correct initialization order
666		Session: pallet_partner_chains_session,
667		NativeTokenManagement: pallet_native_token_management,
668		GovernedMap: pallet_governed_map,
669		TestHelperPallet: crate::test_helper_pallet,
670	}
671);
672
673/// The address format for describing accounts.
674pub type Address = sp_runtime::MultiAddress<AccountId, ()>;
675/// Block header type as expected by this runtime.
676pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
677/// Block type as expected by this runtime.
678pub type Block = generic::Block<Header, UncheckedExtrinsic>;
679/// The SignedExtension to the basic transaction logic.
680pub type SignedExtra = (
681	frame_system::CheckNonZeroSender<Runtime>,
682	frame_system::CheckSpecVersion<Runtime>,
683	frame_system::CheckTxVersion<Runtime>,
684	frame_system::CheckGenesis<Runtime>,
685	frame_system::CheckEra<Runtime>,
686	frame_system::CheckNonce<Runtime>,
687	frame_system::CheckWeight<Runtime>,
688	pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
689);
690
691/// Unchecked extrinsic type as expected by this runtime.
692pub type UncheckedExtrinsic =
693	generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
694/// The payload being signed in transactions.
695pub type SignedPayload = generic::SignedPayload<RuntimeCall, SignedExtra>;
696pub type Migrations = (
697	pallet_session_validator_management::migrations::v1::LegacyToV1Migration<Runtime>,
698	// More migrations can be added here
699);
700/// Executive: handles dispatch to the various modules.
701pub type Executive = frame_executive::Executive<
702	Runtime,
703	Block,
704	frame_system::ChainContext<Runtime>,
705	Runtime,
706	AllPalletsWithSystem,
707	Migrations,
708>;
709
710#[cfg(feature = "runtime-benchmarks")]
711mod benches {
712	define_benchmarks!(
713		[frame_benchmarking, BaselineBench::<Runtime>]
714		[frame_system, SystemBench::<Runtime>]
715		[pallet_balances, Balances]
716		[pallet_timestamp, Timestamp]
717		[pallet_sudo, Sudo]
718		[pallet_native_token_management, NativeTokenManagement]
719		[pallet_block_production_log, BlockProductionLog]
720		[pallet_address_associations, AddressAssociations]
721		[pallet_block_producer_fees, BlockProducerFees]
722		[pallet_block_producer_metadata, BlockProducerMetadata]
723		[pallet_block_participation, BlockParticipation]
724		[pallet_governed_map, GovernedMap]
725	);
726}
727
728impl_runtime_apis! {
729	impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
730		fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
731			build_state::<RuntimeGenesisConfig>(config)
732		}
733
734		fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
735			get_preset::<RuntimeGenesisConfig>(id, crate::genesis_config_presets::get_preset)
736		}
737
738		fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
739			crate::genesis_config_presets::preset_names()
740		}
741	}
742
743	impl sp_api::Core<Block> for Runtime {
744		fn version() -> RuntimeVersion {
745			VERSION
746		}
747
748		fn execute_block(block: Block) {
749			Executive::execute_block(block);
750		}
751
752		fn initialize_block(header: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
753			Executive::initialize_block(header)
754		}
755	}
756
757	impl sp_api::Metadata<Block> for Runtime {
758		fn metadata() -> OpaqueMetadata {
759			OpaqueMetadata::new(Runtime::metadata().into())
760		}
761
762		fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
763			Runtime::metadata_at_version(version)
764		}
765
766		fn metadata_versions() -> sp_std::vec::Vec<u32> {
767			Runtime::metadata_versions()
768		}
769	}
770
771	impl sp_block_builder::BlockBuilder<Block> for Runtime {
772		fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {
773			Executive::apply_extrinsic(extrinsic)
774		}
775
776		fn finalize_block() -> <Block as BlockT>::Header {
777			Executive::finalize_block()
778		}
779
780		fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
781			data.create_extrinsics()
782		}
783
784		fn check_inherents(
785			block: Block,
786			data: sp_inherents::InherentData,
787		) -> sp_inherents::CheckInherentsResult {
788			data.check_extrinsics(&block)
789		}
790	}
791
792	impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
793		fn validate_transaction(
794			source: TransactionSource,
795			tx: <Block as BlockT>::Extrinsic,
796			block_hash: <Block as BlockT>::Hash,
797		) -> TransactionValidity {
798			Executive::validate_transaction(source, tx, block_hash)
799		}
800	}
801
802	impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
803		fn offchain_worker(header: &<Block as BlockT>::Header) {
804			Executive::offchain_worker(header)
805		}
806	}
807
808	impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
809		fn slot_duration() -> sp_consensus_aura::SlotDuration {
810			sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration())
811		}
812
813		fn authorities() -> Vec<AuraId> {
814			pallet_aura::Authorities::<Runtime>::get().into_inner()
815		}
816	}
817
818	impl sp_session::SessionKeys<Block> for Runtime {
819		fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
820			// despite being named "generate" this function also adds generated keys to local keystore
821			opaque::CrossChainKey::generate(seed.clone());
822			opaque::SessionKeys::generate(seed)
823		}
824
825		fn decode_session_keys(
826			encoded: Vec<u8>,
827		) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {
828			opaque::SessionKeys::decode_into_raw_public_keys(&encoded)
829		}
830	}
831
832	impl sp_consensus_grandpa::GrandpaApi<Block> for Runtime {
833		fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList {
834			Grandpa::grandpa_authorities()
835		}
836
837		fn current_set_id() -> sp_consensus_grandpa::SetId {
838			Grandpa::current_set_id()
839		}
840
841		fn submit_report_equivocation_unsigned_extrinsic(
842			_equivocation_proof: sp_consensus_grandpa::EquivocationProof<
843				<Block as BlockT>::Hash,
844				NumberFor<Block>,
845			>,
846			_key_owner_proof: sp_consensus_grandpa::OpaqueKeyOwnershipProof,
847		) -> Option<()> {
848			None
849		}
850
851		fn generate_key_ownership_proof(
852			_set_id: sp_consensus_grandpa::SetId,
853			_authority_id: GrandpaId,
854		) -> Option<sp_consensus_grandpa::OpaqueKeyOwnershipProof> {
855			// NOTE: this is the only implementation possible since we've
856			// defined our key owner proof type as a bottom type (i.e. a type
857			// with no values).
858			None
859		}
860	}
861
862
863	impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Nonce> for Runtime {
864		fn account_nonce(account: AccountId) -> Nonce {
865			System::account_nonce(account)
866		}
867	}
868
869	impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {
870		fn query_info(
871			uxt: <Block as BlockT>::Extrinsic,
872			len: u32,
873		) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
874			TransactionPayment::query_info(uxt, len)
875		}
876		fn query_fee_details(
877			uxt: <Block as BlockT>::Extrinsic,
878			len: u32,
879		) -> pallet_transaction_payment::FeeDetails<Balance> {
880			TransactionPayment::query_fee_details(uxt, len)
881		}
882		fn query_weight_to_fee(weight: sp_weights::Weight) -> Balance {
883			TransactionPayment::weight_to_fee(weight)
884		}
885		fn query_length_to_fee(length: u32) -> Balance {
886			TransactionPayment::length_to_fee(length)
887		}
888	}
889
890	impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi<Block, Balance, RuntimeCall>
891		for Runtime
892	{
893		fn query_call_info(
894			call: RuntimeCall,
895			len: u32,
896		) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> {
897			TransactionPayment::query_call_info(call, len)
898		}
899		fn query_call_fee_details(
900			call: RuntimeCall,
901			len: u32,
902		) -> pallet_transaction_payment::FeeDetails<Balance> {
903			TransactionPayment::query_call_fee_details(call, len)
904		}
905		fn query_weight_to_fee(weight: sp_weights::Weight) -> Balance {
906			TransactionPayment::weight_to_fee(weight)
907		}
908		fn query_length_to_fee(length: u32) -> Balance {
909			TransactionPayment::length_to_fee(length)
910		}
911	}
912
913	#[cfg(feature = "runtime-benchmarks")]
914	impl frame_benchmarking::Benchmark<Block> for Runtime {
915		fn benchmark_metadata(extra: bool) -> (
916			Vec<frame_benchmarking::BenchmarkList>,
917			Vec<frame_support::traits::StorageInfo>,
918		) {
919			use frame_benchmarking::{baseline, BenchmarkList};
920			use frame_support::traits::StorageInfoTrait;
921			use frame_system_benchmarking::Pallet as SystemBench;
922			use baseline::Pallet as BaselineBench;
923
924			let mut list = Vec::<BenchmarkList>::new();
925			list_benchmarks!(list, extra);
926
927			let storage_info = AllPalletsWithSystem::storage_info();
928
929			(list, storage_info)
930		}
931
932		fn dispatch_benchmark(
933			config: frame_benchmarking::BenchmarkConfig
934		) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, alloc::string::String> {
935			use frame_benchmarking::{baseline, BenchmarkBatch};
936			use sp_storage::TrackedStorageKey;
937			use frame_system_benchmarking::Pallet as SystemBench;
938			use baseline::Pallet as BaselineBench;
939			use frame_support::traits::WhitelistedStorageKeys;
940
941			#[allow(non_local_definitions)]
942			impl frame_system_benchmarking::Config for Runtime {}
943			#[allow(non_local_definitions)]
944			impl frame_benchmarking::baseline::Config for Runtime {}
945
946			let whitelist: Vec<TrackedStorageKey> = AllPalletsWithSystem::whitelisted_storage_keys();
947
948			let mut batches = Vec::<BenchmarkBatch>::new();
949			let params = (&config, &whitelist);
950			add_benchmarks!(params, batches);
951
952			Ok(batches)
953		}
954	}
955
956	#[cfg(feature = "try-runtime")]
957	impl frame_try_runtime::TryRuntime<Block> for Runtime {
958		fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) {
959			// NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to
960			// have a backtrace here. If any of the pre/post migration checks fail, we shall stop
961			// right here and right now.
962			let weight = Executive::try_runtime_upgrade(checks).unwrap();
963			(weight, BlockWeights::get().max_block)
964		}
965
966		fn execute_block(
967			block: Block,
968			state_root_check: bool,
969			signature_check: bool,
970			select: frame_try_runtime::TryStateSelect
971		) -> Weight {
972			// NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to
973			// have a backtrace here.
974			Executive::try_execute_block(block, state_root_check, signature_check, select).expect("execute-block failed")
975		}
976	}
977
978	impl sp_sidechain::GetGenesisUtxo<Block> for Runtime {
979		fn genesis_utxo() -> UtxoId {
980			Sidechain::genesis_utxo()
981		}
982	}
983
984	impl sp_sidechain::GetSidechainStatus<Block> for Runtime {
985		fn get_sidechain_status() -> SidechainStatus {
986			SidechainStatus {
987				epoch: Sidechain::current_epoch_number(),
988				slot: ScSlotNumber(*pallet_aura::CurrentSlot::<Runtime>::get()),
989				slots_per_epoch: Sidechain::slots_per_epoch().0,
990			}
991		}
992	}
993
994	impl sidechain_slots::SlotApi<Block> for Runtime {
995		fn slot_config() -> sidechain_slots::ScSlotConfig {
996			sidechain_slots::ScSlotConfig {
997				slots_per_epoch: Sidechain::slots_per_epoch(),
998				slot_duration: <Self as sp_consensus_aura::runtime_decl_for_aura_api::AuraApi<Block, AuraId>>::slot_duration()
999			}
1000		}
1001	}
1002
1003	impl sp_block_producer_metadata::BlockProducerMetadataApi<Block, BlockProducerMetadataType> for Runtime
1004	{
1005		fn get_metadata_for(
1006			cross_chain_pub_key: &CrossChainPublicKey,
1007		) -> Option<BlockProducerMetadataType> {
1008			BlockProducerMetadata::get_metadata_for(&cross_chain_pub_key)
1009		}
1010	}
1011
1012	impl sp_block_producer_fees::BlockProducerFeesApi<Block, AccountId> for Runtime
1013	{
1014		fn get_all_fees() -> Vec<(AccountId, sp_block_producer_fees::PerTenThousands)> {
1015			BlockProducerFees::get_all_latest().map(|(account_id, (_slot, fee))| (account_id, fee)).collect()
1016		}
1017	}
1018
1019	#[api_version(2)]
1020	impl sp_session_validator_management::SessionValidatorManagementApi<
1021		Block,
1022		CommitteeMember<CrossChainPublic, SessionKeys>,
1023		AuthoritySelectionInputs,
1024		sidechain_domain::ScEpochNumber
1025	> for Runtime {
1026		fn get_current_committee() -> (ScEpochNumber, Vec<CommitteeMember<CrossChainPublic, SessionKeys>>) {
1027			SessionCommitteeManagement::current_committee_storage().as_pair()
1028		}
1029		fn get_next_committee() -> Option<(ScEpochNumber, Vec<CommitteeMember<CrossChainPublic, SessionKeys>>)> {
1030			Some(SessionCommitteeManagement::next_committee_storage()?.as_pair())
1031		}
1032		fn get_next_unset_epoch_number() -> sidechain_domain::ScEpochNumber {
1033			SessionCommitteeManagement::get_next_unset_epoch_number()
1034		}
1035		fn calculate_committee(authority_selection_inputs: AuthoritySelectionInputs, sidechain_epoch: ScEpochNumber) -> Option<Vec<CommitteeMember<CrossChainPublic, SessionKeys>>> {
1036			SessionCommitteeManagement::calculate_committee(authority_selection_inputs, sidechain_epoch)
1037		}
1038		fn get_main_chain_scripts() -> sp_session_validator_management::MainChainScripts {
1039			SessionCommitteeManagement::get_main_chain_scripts()
1040		}
1041	}
1042
1043	impl authority_selection_inherents::filter_invalid_candidates::CandidateValidationApi<Block> for Runtime {
1044		fn validate_registered_candidate_data(stake_pool_public_key: &StakePoolPublicKey, registration_data: &RegistrationData) -> Option<RegistrationDataError> {
1045			authority_selection_inherents::filter_invalid_candidates::validate_registration_data(stake_pool_public_key, registration_data, Sidechain::genesis_utxo()).err()
1046		}
1047		fn validate_stake(stake: Option<StakeDelegation>) -> Option<StakeError> {
1048			authority_selection_inherents::filter_invalid_candidates::validate_stake(stake).err()
1049		}
1050		fn validate_permissioned_candidate_data(candidate: PermissionedCandidateData) -> Option<PermissionedCandidateDataError> {
1051			validate_permissioned_candidate_data::<CrossChainPublic>(candidate).err()
1052		}
1053	}
1054
1055	impl sp_native_token_management::NativeTokenManagementApi<Block> for Runtime {
1056		fn get_main_chain_scripts() -> Option<sp_native_token_management::MainChainScripts> {
1057			NativeTokenManagement::get_main_chain_scripts()
1058		}
1059		fn initialized() -> bool {
1060			NativeTokenManagement::initialized()
1061		}
1062	}
1063
1064	impl sp_block_production_log::BlockProductionLogApi<Block, CommitteeMember<CrossChainPublic, SessionKeys>>  for Runtime {
1065		fn get_author(slot: Slot) -> Option<CommitteeMember<CrossChainPublic, SessionKeys>> {
1066			 SessionCommitteeManagement::get_current_authority_round_robin(*slot as usize)
1067		}
1068	}
1069
1070	impl sp_block_participation::BlockParticipationApi<Block, BlockAuthor> for Runtime {
1071		fn should_release_data(slot: Slot) -> Option<Slot> {
1072			BlockParticipation::should_release_data(slot)
1073		}
1074		fn blocks_produced_up_to_slot(slot: Slot) -> Vec<(Slot, BlockAuthor)> {
1075			<Runtime as pallet_block_participation::Config>::blocks_produced_up_to_slot(slot).collect()
1076		}
1077		fn target_inherent_id() -> InherentIdentifier {
1078			<Runtime as pallet_block_participation::Config>::TARGET_INHERENT_ID
1079		}
1080	}
1081
1082	impl sp_governed_map::GovernedMapIDPApi<Block> for Runtime {
1083		fn is_initialized() -> bool {
1084			GovernedMap::is_initialized()
1085		}
1086		fn get_current_state() -> BTreeMap<String, ByteString> {
1087			GovernedMap::get_all_key_value_pairs_unbounded().collect()
1088		}
1089		fn get_main_chain_scripts() -> Option<MainChainScriptsV1> {
1090			GovernedMap::get_main_chain_scripts()
1091		}
1092		fn get_pallet_version() -> u32 {
1093			GovernedMap::get_version()
1094		}
1095	}
1096}
1097
1098#[cfg(test)]
1099mod tests {
1100	use crate::mock::*;
1101	use frame_support::{
1102		dispatch::PostDispatchInfo,
1103		inherent::ProvideInherent,
1104		traits::{UnfilteredDispatchable, WhitelistedStorageKeys},
1105	};
1106	use sp_core::{Pair, hexdisplay::HexDisplay};
1107	use sp_inherents::InherentData;
1108	use std::collections::HashSet;
1109
1110	#[test]
1111	fn check_whitelist() {
1112		let whitelist: HashSet<String> = super::AllPalletsWithSystem::whitelisted_storage_keys()
1113			.iter()
1114			.map(|e| HexDisplay::from(&e.key).to_string())
1115			.collect();
1116
1117		// Block Number
1118		assert!(
1119			whitelist.contains("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac")
1120		);
1121		// Total Issuance
1122		assert!(
1123			whitelist.contains("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80")
1124		);
1125		// Execution Phase
1126		assert!(
1127			whitelist.contains("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a")
1128		);
1129		// Event Count
1130		assert!(
1131			whitelist.contains("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850")
1132		);
1133		// System Events
1134		assert!(
1135			whitelist.contains("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7")
1136		);
1137	}
1138
1139	// The set committee takes effect next session. Committee can be set for 1 session in advance.
1140	#[test]
1141	fn check_grandpa_authorities_rotation() {
1142		new_test_ext().execute_with(|| {
1143			// Needs to be run to initialize first slot and epoch numbers;
1144			advance_block();
1145			set_committee_through_inherent_data(&[alice()]);
1146			until_epoch_after_finalizing(1, &|| {
1147				assert_current_epoch!(0);
1148				assert_grandpa_weights();
1149				assert_grandpa_authorities!([alice(), bob()]);
1150			});
1151
1152			set_committee_through_inherent_data(&[bob()]);
1153			for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1154				assert_current_epoch!(1);
1155				assert_grandpa_weights();
1156				assert_grandpa_authorities!([alice()]);
1157			});
1158
1159			for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH, &|| {
1160				assert_current_epoch!(2);
1161				assert_grandpa_weights();
1162				assert_grandpa_authorities!([bob()]);
1163			});
1164
1165			// Authorities can be set as late as in the first block of new epoch, but it makes session last 1 block longer
1166			set_committee_through_inherent_data(&[alice()]);
1167			advance_block();
1168			assert_current_epoch!(3);
1169			assert_grandpa_authorities!([bob()]);
1170			set_committee_through_inherent_data(&[alice(), bob()]);
1171			for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH - 1, &|| {
1172				assert_current_epoch!(3);
1173				assert_grandpa_weights();
1174				assert_grandpa_authorities!([alice()]);
1175			});
1176
1177			for_next_n_blocks_after_finalizing(SLOTS_PER_EPOCH * 3, &|| {
1178				assert_grandpa_weights();
1179				assert_grandpa_authorities!([alice(), bob()]);
1180			});
1181		});
1182
1183		fn assert_grandpa_weights() {
1184			Grandpa::grandpa_authorities()
1185				.into_iter()
1186				.for_each(|(_, weight)| assert_eq!(weight, 1))
1187		}
1188	}
1189
1190	// The set committee takes effect next session. Committee can be set for 1 session in advance.
1191	#[test]
1192	fn check_aura_authorities_rotation() {
1193		new_test_ext().execute_with(|| {
1194			advance_block();
1195			set_committee_through_inherent_data(&[alice()]);
1196			until_epoch(1, &|| {
1197				assert_current_epoch!(0);
1198				assert_aura_authorities!([alice(), bob()]);
1199			});
1200
1201			for_next_n_blocks(SLOTS_PER_EPOCH, &|| {
1202				assert_current_epoch!(1);
1203				assert_aura_authorities!([alice()]);
1204			});
1205
1206			// Authorities can be set as late as in the first block of new epoch, but it makes session last 1 block longer
1207			set_committee_through_inherent_data(&[bob()]);
1208			assert_current_epoch!(2);
1209			assert_aura_authorities!([alice()]);
1210			advance_block();
1211			set_committee_through_inherent_data(&[alice(), bob()]);
1212			for_next_n_blocks(SLOTS_PER_EPOCH - 1, &|| {
1213				assert_current_epoch!(2);
1214				assert_aura_authorities!([bob()]);
1215			});
1216
1217			set_committee_through_inherent_data(&[alice(), bob()]);
1218			for_next_n_blocks(SLOTS_PER_EPOCH * 3, &|| {
1219				assert_aura_authorities!([alice(), bob()]);
1220			});
1221		});
1222	}
1223
1224	// The set committee takes effect at next session. Committee can be set for 1 session in advance.
1225	#[test]
1226	fn check_cross_chain_committee_rotation() {
1227		new_test_ext().execute_with(|| {
1228			advance_block();
1229			set_committee_through_inherent_data(&[alice()]);
1230			until_epoch(1, &|| {
1231				assert_current_epoch!(0);
1232				assert_next_committee!([alice()]);
1233			});
1234
1235			set_committee_through_inherent_data(&[bob()]);
1236			for_next_n_blocks(SLOTS_PER_EPOCH, &|| {
1237				assert_current_epoch!(1);
1238				assert_next_committee!([bob()]);
1239			});
1240
1241			set_committee_through_inherent_data(&[]);
1242			for_next_n_blocks(SLOTS_PER_EPOCH, &|| {
1243				assert_current_epoch!(2);
1244				assert_next_committee!([bob()]);
1245			});
1246		});
1247	}
1248
1249	pub fn set_committee_through_inherent_data(
1250		expected_authorities: &[TestKeys],
1251	) -> PostDispatchInfo {
1252		let epoch = Sidechain::current_epoch_number();
1253		let slot = *pallet_aura::CurrentSlot::<Test>::get();
1254		println!(
1255			"(slot {slot}, epoch {epoch}) Setting {} authorities for next epoch",
1256			expected_authorities.len()
1257		);
1258		let inherent_data_struct = create_inherent_data_struct(expected_authorities);
1259		let mut inherent_data = InherentData::new();
1260		inherent_data
1261			.put_data(
1262				SessionCommitteeManagement::INHERENT_IDENTIFIER,
1263				&inherent_data_struct.data.unwrap(),
1264			)
1265			.expect("Setting inherent data should not fail");
1266		let call = <SessionCommitteeManagement as ProvideInherent>::create_inherent(&inherent_data)
1267			.expect("Creating test inherent should not fail");
1268		println!("    inherent: {:?}", call);
1269		call.dispatch_bypass_filter(RuntimeOrigin::none())
1270			.expect("dispatching test call should work")
1271	}
1272}