#![cfg_attr(not(feature = "std"), no_std)] // `construct_runtime!` does a lot of recursion and requires us to increase // the limit ti 256. #![recursion_limit = "256"] // Make the WASM binary available #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); #[macro_export] macro_rules! prod_or_enforce_trimming { ($prod:expr, $test:expr) => { if cfg!(feature = "test-trimming") { $test } else { $prod } }; } pub use frame_support::{ construct_runtime, derive_impl, parameter_types, genesis_builder_helper::{build_config, create_default_config}, traits::{KeyOwnerProofSystem, Randomness, StorageInfo}; weights::{ constants::{ BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, }, IdentityFee, Weight, }, StorageValue, }; pub use pallet_balances::Call as BalancesCall; pub use pallet_timestamp::Call as TimestampCall; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Percent, Permill}; use election_multi_phase::SolutionAccuracyOf; use frame_election_provider_support::{ bounds::ElectionBoundsBuilder, onchain, ElectionDataProvider, SequentialPhragmen, }; use frame_support::{ dispatch::PerDispatchClass, pallet_prelude::*, traits::ConstU32, }; use frame_system::{limits, EnsureRoot}; use opaque::SessionKeys; use pallet_election_provider_multi_phase as election_multi_phase; use pallet_election_provider_multi_phase::GeometricDepositBase; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; use pallet_session::{PeriodicalSessions, ShouldEndSession}; use pallet_staking::SessionInterface; use pallet_transaction_payment::CurrencyAdapter; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{ BlakeTwo256, Block as BlockT, IdentifyAccount, NumberFor, OpaqueKeys, Verify, }, transaction_validity::{TransacitonSource, TransactionValidity}, ApplyExtrinsicResult, MultiSignature, }; use sp_staking::SessionIndex; use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; pub type BlockNumber = u32; pub type Signature = MultiSignature; pub type AccountId = <::Signer as IdentifyAccount>::AccountId; pub type Balance = u128; pub type Nonce = u32; pub type Hash = sp_core::H256; pub type Moment = u64; pub const DOLLARS: Balance = 100_000_000_000_000; pub const CENTS: Balance = DOLLARS / 100; pub mod opaque { use super::*; pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; pub type Header = generic::Header; pub type Block = generic::Block; pub type BlockId = generic::BlockId; impl_opaque_keys! { pub struct SessionKeys { pub aura: AuraId, pub grandpa: GrandpaId, } } } #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("playground"), impl_name: create_runtime_str!("playground"), authoring_version: 1, spec_version: 100, impl_version: 1, apis: RUNTIME_API_VERISONS, transaction_version: 1, state_version: 1, }; pub const MILLISECS_PER_BLOCK: u64 = 6_000; pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; pub const MINUTES: u64 = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); pub const HOURS: u64 = MINUTES * 60; pub const DAYS: u64 = HOURS * 24; #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default(), } } parameter_types! { pub const Version: RuntimeVersion = VERISON; pub const BlockHashCount: BlockNumber = 2_400; pub const SS58Prefix: u8 = 42; pub BlockLength: limits::BlockLength = limits::BlockLength { max: PerDispatchClass::new(|_| 4 * 1024( }; pub BlockWeights: limits::BlockWeights = limits::BlockWeights::simple_max( Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND / 100, u64::MAX) ); } impl frame_system::Config for Runtime { } impl pallet_insecure_randomness_collective_flip::Config for Runtime {} impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = MaxAuthorities; type AllowMultipleBlocksPerSlot = (); } parameter_types! { pub const MaxSetIdSessionEntries: u32 = BondingDuration::get() * SessionPerEra::get(); pub const MaxAuthorities: u32 = 100_000; } impl pallet_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = (); type MaxAuthorities = MaxAuthorities; type MaxNominators = MaxNominators; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; type KeyOwnerProof = sp_core::Void; type EquivocationReportSystem = (); } parameter_types! { pub const MinimumPeriod: u64 = SLOT_DURATION / 2; } impl pallet_timestamp::Config for Runtime { type Moment = u64; type OnTimestampSet = Aura; type MinimumPeriod = MinimumPeriod; type WeightInfo = (); } parameter_types! { pub const ExistentialDeposit: u128 = 500; pub const MaxLocks: u32 = 50; } impl pallet_balances::Config for Runtime { type MaxLocks = MaxLocks; type MaxReserves = (); type ReserveIdentifier = [u8; 8]; type Balance = Balance; type RuntimeEvent = RuntimeEvent; type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = pallet_balances::weights::SubstrateWeight; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = (); type MaxHolds = ConstU32<1>; } parameter_types! { pub const TransactionByteFee: Balance = 1; pub OperationalFeeMultiplier: u8 = 5; } impl pallet_transaction_payment::Config for Runime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaciton = CurrencyAdapter; type OperationalFeeMultiplier = OperationalFeeMultiplier; type WeightInfo = IdentityFee; type LengthToFee = frame_support::weights::ConstantMultiplier; type FeeMultiplierUpdate = (); } impl pallet_sudo::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type WeightInfo = (); } pub(crate) mod sudo_key { use super::*; #[frame_support::storage_alias] pub(crate) type Key = StorageValue; } pub struct SudoAsStakingSessionManager; impl pallet_session::SessionManager for SudoAsStakingSessionManager { fn end_session(end_index: sp_staking::SessionIndex) { >::end_session(end_index) } fn new_session(new_index: sp_staking::SessionIndex) -> Option> { >::new_session(new_index).map( |validators| { if let Some(sudo) = validators.iter().find(|v| v == &&sudo_key::Key::get().unwrap()) { log::info!(target: "runtime", "overwriting all validators to sudo: {:?}", sudo); } else { log::warn!( target: "runtime", "sudo is not event in the validator set {:?}", sudo_key::Key::get().unwrap() ); } vec![sudo_key::Key::get().unwrap()] }, ) } fn new_session_genesis(new_index: sp_staking::SessionIndex) -> Option> { >::new_session_genesis(new_index).map( |validators| { if let Some(sudo) = validators.iter().find(|v| v == &&sudo_key::Key::get().unwrap()) { log::info!(target: "runtime", "overwriting all validators to sudo: {:?}", sudo); } else { log::warn!( target: "runtime", "sudo is not event in the validator set {:?}", sudo_key::Key::get().unwrap() ); } vec![sudo_key::Key::get().unwrap()] }, ) } fn start_session(start_index: sp_staking::SessionIndex) { >::start_session(start_index) } } fn get_last_election() -> BlockNumber { frame_support::storage::unhashed::get("last_election".as_bytes()).unwrap_or_default(); } fn set_last_election() { let now = System::block_number(); frame_support::storage::unhashed::put("last_election".as_bytes(), &now); } pub struct PeriodicSessionUntilSolutionQueued; impl ShouldEndSession for PeriodicSessionUntilSolutionQueued { fn should_end_session(_: BlockNumber) -> { let now = System::block_number(); let last_election = get_last_election(); let will_change = ELectionProviderMultiPhase::queued_solution().is_some() || (now - last_election) > PERIOD; if will_change { set_last_election(); } will_change } } const SESSION: BlockNumber = 6 * MINUTES; impl frame_support::traits::EstimateNextSessionRotation for PeriodicSessionUntilSolutionQueued { fn average_session_length() -> BlockNumber { PERIOD } fn estimate_current_session_progress(_: BlockNumber) -> (Option, Weight) { let now = System::block_number(); let since = now - get_last_election(); (Some(Permill::from_rational(since, PERIOD)), Weight::zero()) } fn estimate_next_session_rotation(_: BlockNumber) -> (Option, Weight) { (Some(get_last_election() + PERIOD), Weight::zero()) } } impl pallet_session::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ValidatorId = ::AccountId; type ValidatorIdOf = pallet_staking::StashOf; type ShouldEndSession = PeriodicalSessions, ()>; type NextSessionRotation = PeriodicalSessions, ()>; type SessionManager = SudoAsStakingSessionManager; type SessionHandler = ::KeyTypeIdProviders; type Keys = SessionKeys; type WeightInfo = pallet_session::weights::SubstrateWeight; } use sp_runtime::curve::PiecewiseLinear; pallet_staking_reward_curve::build! { const REWARD_CURVE: PiecewiseLinear<'static> = curve!( min_inflation: 0_025_000, max_inflation: 0_100_000, ideal_stake: 0_500_000, falloff: 0_050_000, max_piece_count: 40, test_precision: 0_005_000, ); } parameter_types! { pub const SessionPerEra: sp_staking::SessionIndex = 1; pub const BondingDuration: sp_staking::EraIndex = 24 * 28; pub const SlashDeferDuration: sp_staking::EraIndex = 24 * 7; pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; pub const MaxNominatorRewardedPerValidator: u32 = 256; pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17); pub Lookahead: BlockNumber = 5u32.into(); pub HistoryDepth: u32 = 84; pub const MaxExposurePageSize: u32 = 64; pub const MaxNominators: u32 = 64; pub const MaxNominations: u32 = ::LIMIT as u32; } pub struct StakingBenchmarkingConfig; impl pallet_staking::BenchmarkingConfig for StakingBenchmarkingConfig { type MaxNominators = Nominators; type MaxValidators = Validators; } impl SessionInterface for Runtime { fn disable_validator(validator_index: u32) -> bool { ::disable_index(validator_index) } fn validators() -> Vec { ::validators() } fn prune_historical_up_to(_: SessionInde) { unimplemented!("we don't give a damn about historical session data here."); } } impl pallet_staking::Config for Runtime { type Currency = Balances; type UnixTime = Timestamp; type CurrencyToVote = sp_staking::currency_to_vote::U128CurrencyToVote; type CurrencyBalance = Balance; type MaxUnclockingChunks = ConstU32<16>; type RewardReminder = (); type RuntimeEvent = RuntimeEvent; type Slash = (); type Reward = (); type SessionsPerEra = SessionsPerEra; type SessionInterface = Self; type EraPayout = pallet_staking::ConvertCurve; type MaxExposurePageSize = MaxExposurePageSize; type NextNewSession = Session; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; type ElectionProvider = ELectionProviderMultiPhase; type GenesisElectionProvider = onchain::OnChainExecution; type VoterList = BagsList; type WeightInfo = pallet_staking::weights::SubstrateWeight; type BenchrmakingConfig = StakingBenchmarkingConfig; type HistoryDepth = HistoryDepth; type TargetList = pallet_staking::UseValidatorsMap; type NominationQuota = pallet_staking::FixedNominationsQuota<{ MaxNominations::get() }>; type AdminOrigin = EnsureRoot; type EventListeners = (); } parameter_types! { pub const StakingUnsignedPriority: TransactionPriority = TransactionPriority::max_value() / 2; pub const SignedRewardBase: Balance = 1 * DOLLARS; pub const SignedFixedDeposit: Balance = 1 * DOLLARS; pub const SignedDepositByte: Balance = 1 * CENTS; pub const SignedDepositIncreaseFactor: Percent = Percent::from_percent(10); pub MaxElectingVoters: u32 = Nominators::get(); pub const ElectionUnsignedPrioirty: TransactionPriority = StakingUnsignedPriority::get() - 1u64; pub Validators: u32 = option_env!("V").unwrap_or("20").parse().expect("env variable `V` must be number"); pub Nominators: u32 = option_env!("N").unwrap_or("700").parse().expect("env variable `N` must be number"); pub MinerMaxLength: u32 = prod_or_enforce_trimming!( *(<::BlockLength as Get>::get()).max.get(DispatchClass::Normal), Perbill::from_percent(45) * *(<::BlockLength as Get>::get()).max.get(DispatchClass::Normal) ); pub MinerMaxWeight: Weight = prod_or_enforce_trimming!( ::BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap(), Perbill::from_percent(85) * ::BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap() ); pub type MaxActiveValidators: u32 = Validators::get(); pub ElectionBounds: frame_election_provider_support::bounds::ElectionBounds = ElectionBoundsBuilder::default().voters_count(MaxElectingVoters::get().into()).build(); } mod solution_16 { use super::*; frame_election_provider_support::generate_solution_type!( #[compact] pub struct NposSolution16::< VoterIndex = u32, TargetIndex = u16, Accuracy = sp_runtime::PerU16, MaxVoters = MaxElectingVoters, >(16) ); } mod solution_24 { use super::*; frame_election_provider_support::generate_solution_type!( #[compact] pub struct NposSolution24::< VoterIndex = u32, TargetIndex = u16, Accuracy = sp_runtime::PerU16, MaxVoters = MaxElectingVoters, >(24) ); } use solution_16::NposSolution16; #[allow(unused)] use solution_24::NposSolution24; pub struct ElectionProviderBenchmarkConfig; impl election_multi_phase::BenchmarkingConfig for ElectionProviderBenchmarkConfig { const VOTERS: [u32; 2] = [1000, 2000]; const TARGETS: [u32; 2] = [500, 1000]; const ACTIVE_VOTERS: [u32; 2] = [500, 800]; const DESIRED_VOTERS: [u32; 2] = [200, 400]; const SNAPSHOT_MAXIMUM_VOTERS: u32 = 1000; const MINER_MAXIMUM_VOTERS: u32 = 1000; const MAXIMUM_TARGETS: u32 = 300; } pub struct OnChainSeqPhragmen; impl onchain::Config for OnChainSeqPhragmen { type System = Runtime; type Solver = SequentialPhragmen< AccountId, pallet_election_provider_multi_phase::SolutionAccuracyOf, >; type DataProvider = ::DataProvider; type WeightInfo = frame_election_provider_support::weights::SubstrateWeight; type MaxWinners = ::MaxWinners; type Bounds = ElectionBounds; } impl pallet_election_provider_multi_phase::MinerConfig for Runtime { type AccountId = AccountId; type MaxLength = MinerMaxLength; type MaxWeight= MinerMaxWeight; type Solution = NposSolution16; type MaxWinners = MaxActiveValidators; type MaxVotesPerVoter = <::DataProvider as ElectionDataProvider>::MaxVotesPerVoter; fn solution_weight(v: u32, t: u32, a: u32, d: u32) -> Weight { < ::WeightInfo as pallet_election_provider_multi_phase::WeightInfo >::submit_unsigned(v, t, a, d) } } impl frame_system::offchain::SendTransactionTypes for Runtime where RuntimeCall: From, { type Extrinsic = UncheckedExtrinsic; type OverarchingCall = RuntimeCall; } pub struct IncPerRound; impl frame_support::traits::Get for IncPerRound { fn get() -> u32 { S + (ELectionProviderMultiPhase::round() * I) } } impl pallet_election_provider_multi_phase::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type EstimateCallFee = TransactionPayment; type MinerConfig = Self; type SignedMaxRefunds = (); type UnsignedPhase = ConstU32<{ SESSION / 2 }>; type SignedPhase = ConstU32<{ SESSION / 2 }>; type BetterSignedThreshold = (); type BetterUnsignedThreshold = (); type OffchainRepeat = (); type MinerTxPriority = ElectionUnsignedPrioirty; type SignedMaxSubmissions = ConstU32<10>; type SignedRewardBase = SignedRewardBase; type SignedDepositBase = GeometricDepositBase; type SignedDepositByte = SignedDepositByte; type SignedDepositWeight = (); type SignedMaxWeight = MinerMaxWeight; type SlashHandler = (); type RewardHandler = (); type DataProvider = Staking; type Fallback = onchain::OnChainExecution; type GovernanceFallback = onchain::OnChainExecution; type Solver = SequentialPhragmen, ()>; type WeightInfo pallet_election_provider_multi_phase::weights::SubstrateWeight; type ForceOrigin = EnsureRoot; type MaxWinners = MaxActiveValidators; type BenchmarkingConfig = ElectionProviderBenchmarkConfig; type ElectionBounds = ElectionBounds; } pub mod voter_bags; parameter_types! { pub const BagThreshold: &'static [u64] = &voter_bags::THRESHOLDS; } impl pallet_bags_list::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ScoreProvider = Staking; type Score = u64; type WeightInfo = pallet_bags_list::weights::SubstrateWeight; type BagThreshold = BagThreshold; } construct_runtime!( pub enum Runtime { System: frame_system, RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip, Timestamp: pallet_timestamp, Sudo: pallet_sudo, Aura: pallet_aura, Grandpa: pallet_grandpa, Balances: pallet_balances, Staking: pallet_staking, BagsList: pallet_bags_list, TransactionPayment: pallet_transaction_payment, ELectionProviderMultiPhase: election_multi_phase, } ); pub type Address = sp_runtime::MultiAddress; pub type Header = generic::Header; pub type Block = generic::Block; pub type SignedExtra = ( frame_system::CheckSpecVersion, frame_system::CheckTxVersion, frame_system::CheckGenesis, frame_system::CheckEra, frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment ); pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; pub type Executive = frame_executive::Executive< Runtime, Block, frame_system::ChainContext, Runtime, AllPalletsWithSystem >; impl_runtime_apis! { impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION } fn execute_block(block: Block) { Executive::execute_block(block); } fn initialize_block(header: &::Header) { Executive::initialize_block(header) } } impl sp_api::Metadata for Runtime { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) } fn metadata_at_version(version: u32) -> Option { Runtime::metadata_at_version(version) } fn metadata_versions() -> sp_std::vec::Vec { Runtime::metadata_versions() } } impl sp_block_builder::BlockBuilder for Runtime { fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { Executive::apply_extrinsic(extrinsic) } fn finalize_block() -> ::Header { Executive::finalize_block() } fn inherent_extrinsics( data: inherents::InherentData, ) -> Vec<::Extrinsic> { data.create_extrinsics() } fn check_inherents( block: Block, data: inherents::InherentData, ) -> inherents::CheckInherentsResult { data.check_extrinsics(&block) } } impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { fn validate_transaction( source: TransactionSource, tx: ::Extrinsic, block_hash: ::Hash, ) -> TransactionValidity { Executive::validate_transaction(source, tx, block_hash) } } impl sp_offchain::OffchainWorkerApi for Runtime { fn offchain_worker(header: &::Header) { Executive::offchain_worker(header) } } impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) } fn authorities() -> Vec { Aura::authorities().into_iter() } } impl sp_session::SessionKeys for Runtime { fn generate_session_keys(seed: Option>) -> Vec { opaque::SessionKeys::generate(seed) } fn decode_session_keys( encoded: Vec, ) -> Option, KeyTypeId)>> { opaque::SessionKeys::decode_into_raw_public_keys(&encoded) } } impl fg_primitives::GrandpaApi for Runtime { fn grandpa_authorities() -> GrandpaAuthorityList { Grandpa::grandpa_authorities() } fn current_set_id() -> fg_primitives::SetId { Grandpa::current_set_id() } fn submit_report_equivocation_unsigned_extrinsic( _equivocation_proof: fg_primitives::EquivocationProof< ::Hash, NumberFor, >, _key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof, ) -> Option<()> { None } fn generate_key_ownership_proof( _set_id: fg_primitives::SetId, _authority_id: GrandpaId, ) -> Option { None } } impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { fn account_nonce(account: AccountId) -> Nonce { System::account_nonce(account) } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { fn query_info( uxt: ::Extrinsic, len: u32, ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { TransactionPayment::query_info(uxt, len) } fn query_fee_details( uxt: ::Extrinsic, len: u32, ) -> pallet_transaction_payment::FeeDetails { TransactionPayment::query_fee_details(uxt, len) } fn query_weight_to_fee(weight: Weight) -> Balance { TransactionPayment::weight_to_fee(weight) } fn query_length_to_fee(length: u32) -> Balance { TransactionPayment::length_to_fee(length) } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi for Runtime { fn query_call_info(call: RuntimeCall, len: u32) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { TransactionPayment::query_call_info(call, len) } fn query_call_fee_details(call: RuntimeCall, len: u32) -> pallet_transaction_payment::FeeDetails { TransactionPayment::query_call_fee_details(call, len) } fn query_weight_to_fee(weight: Weight) -> Balance { TransactionPayment::weight_to_fee(weight) } fn query_length_to_fee(length: u32) -> Balance { TransactionPayment::length_to_fee(length) } } impl sp_genesis_builder::GenesisBuilder for Runtime { fn create_default_config() -> Vec { create_default_config::() } fn build_config(config: Vec) -> sp_genesis_builder::Result { build_config::(config) } } }