// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see .
//! Common runtime code for Polkadot and Kusama.
#![cfg_attr(not(feature = "std"), no_std)]
pub mod assigned_slots;
pub mod auctions;
pub mod claims;
pub mod crowdloan;
pub mod elections;
pub mod impls;
pub mod paras_registrar;
pub mod paras_sudo_wrapper;
pub mod purchase;
pub mod slot_range;
pub mod slots;
pub mod traits;
pub mod xcm_sender;
#[cfg(test)]
mod integration_tests;
#[cfg(test)]
mod mock;
pub use frame_support::weights::constants::{
BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight,
};
use frame_support::{
parameter_types,
traits::{ConstU32, Currency, OneSessionHandler},
weights::{constants::WEIGHT_PER_SECOND, DispatchClass, Weight},
};
use frame_system::limits;
use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment};
use primitives::v2::{AssignmentId, BlockNumber, ValidatorId};
use sp_runtime::{FixedPointNumber, Perbill, Perquintill};
use static_assertions::const_assert;
pub use elections::{OffchainSolutionLengthLimit, OffchainSolutionWeightLimit};
pub use pallet_balances::Call as BalancesCall;
#[cfg(feature = "std")]
pub use pallet_staking::StakerStatus;
pub use pallet_timestamp::Call as TimestampCall;
#[cfg(any(feature = "std", test))]
pub use sp_runtime::BuildStorage;
/// Implementations of some helper traits passed into runtime modules as associated types.
pub use impls::ToAuthor;
pub type NegativeImbalance = as Currency<
::AccountId,
>>::NegativeImbalance;
/// We assume that an on-initialize consumes 1% of the weight on average, hence a single extrinsic
/// will not be allowed to consume more than `AvailableBlockRatio - 1%`.
pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(1);
/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used
/// by Operational extrinsics.
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
/// We allow for 2 seconds of compute with a 6 second average block time.
pub const MAXIMUM_BLOCK_WEIGHT: Weight = 2 * WEIGHT_PER_SECOND;
const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct());
// Common constants used in all runtimes.
parameter_types! {
pub const BlockHashCount: BlockNumber = 2400;
/// The portion of the `NORMAL_DISPATCH_RATIO` that we adjust the fees with. Blocks filled less
/// than this will decrease the weight and more will increase.
pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25);
/// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to
/// change the fees more rapidly.
pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000);
/// Minimum amount of the multiplier. This value cannot be too low. A test case should ensure
/// that combined with `AdjustmentVariable`, we can recover from the minimum.
/// See `multiplier_can_grow_from_zero`.
pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128);
/// Maximum length of block. Up to 5MB.
pub BlockLength: limits::BlockLength =
limits::BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
/// Block weights base values and limits.
pub BlockWeights: limits::BlockWeights = limits::BlockWeights::builder()
.base_block(BlockExecutionWeight::get())
.for_class(DispatchClass::all(), |weights| {
weights.base_extrinsic = ExtrinsicBaseWeight::get();
})
.for_class(DispatchClass::Normal, |weights| {
weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT);
})
.for_class(DispatchClass::Operational, |weights| {
weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);
// Operational transactions have an extra reserved space, so that they
// are included even if block reached `MAXIMUM_BLOCK_WEIGHT`.
weights.reserved = Some(
MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT,
);
})
.avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)
.build_or_panic();
}
/// Parameterized slow adjusting fee updated based on
/// https://research.web3.foundation/en/latest/polkadot/overview/2-token-economics.html#-2.-slow-adjusting-mechanism
pub type SlowAdjustingFeeUpdate =
TargetedFeeAdjustment;
/// The type used for currency conversion.
///
/// This must only be used as long as the balance type is `u128`.
pub type CurrencyToVote = frame_support::traits::U128CurrencyToVote;
static_assertions::assert_eq_size!(primitives::v2::Balance, u128);
/// A placeholder since there is currently no provided session key handler for parachain validator
/// keys.
pub struct ParachainSessionKeyPlaceholder(sp_std::marker::PhantomData);
impl sp_runtime::BoundToRuntimeAppPublic for ParachainSessionKeyPlaceholder {
type Public = ValidatorId;
}
impl OneSessionHandler
for ParachainSessionKeyPlaceholder
{
type Key = ValidatorId;
fn on_genesis_session<'a, I: 'a>(_validators: I)
where
I: Iterator- ,
T::AccountId: 'a,
{
}
fn on_new_session<'a, I: 'a>(_changed: bool, _v: I, _q: I)
where
I: Iterator
- ,
T::AccountId: 'a,
{
}
fn on_disabled(_: u32) {}
}
/// A placeholder since there is currently no provided session key handler for parachain validator
/// keys.
pub struct AssignmentSessionKeyPlaceholder(sp_std::marker::PhantomData);
impl sp_runtime::BoundToRuntimeAppPublic for AssignmentSessionKeyPlaceholder {
type Public = AssignmentId;
}
impl OneSessionHandler
for AssignmentSessionKeyPlaceholder
{
type Key = AssignmentId;
fn on_genesis_session<'a, I: 'a>(_validators: I)
where
I: Iterator
- ,
T::AccountId: 'a,
{
}
fn on_new_session<'a, I: 'a>(_changed: bool, _v: I, _q: I)
where
I: Iterator
- ,
T::AccountId: 'a,
{
}
fn on_disabled(_: u32) {}
}
/// A reasonable benchmarking config for staking pallet.
pub struct StakingBenchmarkingConfig;
impl pallet_staking::BenchmarkingConfig for StakingBenchmarkingConfig {
type MaxValidators = ConstU32<1000>;
type MaxNominators = ConstU32<1000>;
}
/// Macro to set a value (e.g. when using the `parameter_types` macro) to either a production value
/// or to an environment variable or testing value (in case the `fast-runtime` feature is selected).
/// Note that the environment variable is evaluated _at compile time_.
///
/// Usage:
/// ```Rust
/// parameter_types! {
/// // Note that the env variable version parameter cannot be const.
/// pub LaunchPeriod: BlockNumber = prod_or_fast!(7 * DAYS, 1, "KSM_LAUNCH_PERIOD");
/// pub const VotingPeriod: BlockNumber = prod_or_fast!(7 * DAYS, 1 * MINUTES);
/// }
#[macro_export]
macro_rules! prod_or_fast {
($prod:expr, $test:expr) => {
if cfg!(feature = "fast-runtime") {
$test
} else {
$prod
}
};
($prod:expr, $test:expr, $env:expr) => {
if cfg!(feature = "fast-runtime") {
core::option_env!($env).map(|s| s.parse().ok()).flatten().unwrap_or($test)
} else {
$prod
}
};
}
#[cfg(test)]
mod multiplier_tests {
use super::*;
use frame_support::{parameter_types, weights::Weight};
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, Convert, IdentityLookup, One},
Perbill,
};
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic;
type Block = frame_system::mocking::MockBlock;
frame_support::construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Config, Storage, Event}
}
);
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const AvailableBlockRatio: Perbill = Perbill::one();
pub BlockLength: frame_system::limits::BlockLength =
frame_system::limits::BlockLength::max(2 * 1024);
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(1024);
}
impl frame_system::Config for Runtime {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = BlockWeights;
type BlockLength = ();
type DbWeight = ();
type Origin = Origin;
type Index = u64;
type BlockNumber = u64;
type Call = Call;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup;
type Header = Header;
type Event = Event;
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
fn run_with_system_weight(w: Weight, mut assertions: F)
where
F: FnMut() -> (),
{
let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::default()
.build_storage::()
.unwrap()
.into();
t.execute_with(|| {
System::set_block_consumed_resources(w, 0);
assertions()
});
}
#[test]
fn multiplier_can_grow_from_zero() {
let minimum_multiplier = MinimumMultiplier::get();
let target = TargetBlockFullness::get() *
BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap();
// if the min is too small, then this will not change, and we are doomed forever.
// the weight is 1/100th bigger than target.
run_with_system_weight(target * 101 / 100, || {
let next = SlowAdjustingFeeUpdate::::convert(minimum_multiplier);
assert!(next > minimum_multiplier, "{:?} !>= {:?}", next, minimum_multiplier);
})
}
#[test]
#[ignore]
fn multiplier_growth_simulator() {
// assume the multiplier is initially set to its minimum. We update it with values twice the
//target (target is 25%, thus 50%) and we see at which point it reaches 1.
let mut multiplier = MinimumMultiplier::get();
let block_weight = TargetBlockFullness::get() *
BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap() *
2;
let mut blocks = 0;
while multiplier <= Multiplier::one() {
run_with_system_weight(block_weight, || {
let next = SlowAdjustingFeeUpdate::::convert(multiplier);
// ensure that it is growing as well.
assert!(next > multiplier, "{:?} !>= {:?}", next, multiplier);
multiplier = next;
});
blocks += 1;
println!("block = {} multiplier {:?}", blocks, multiplier);
}
}
}