Parachains para_inherent.rs to FrameV2 (#3531)

* last parachains migration!

* migrate runtimes

* disable frame supertrait

* add pallet::inherent

* mock

* cargo +nightly fmt
This commit is contained in:
ferrell-code
2021-08-03 07:02:34 -04:00
committed by GitHub
parent f179e8f5e1
commit 24c0d5a51e
7 changed files with 110 additions and 103 deletions
+1 -1
View File
@@ -1488,7 +1488,7 @@ construct_runtime! {
Configuration: parachains_configuration::{Pallet, Call, Storage, Config<T>} = 51, Configuration: parachains_configuration::{Pallet, Call, Storage, Config<T>} = 51,
ParasShared: parachains_shared::{Pallet, Call, Storage} = 52, ParasShared: parachains_shared::{Pallet, Call, Storage} = 52,
ParaInclusion: parachains_inclusion::{Pallet, Call, Storage, Event<T>} = 53, ParaInclusion: parachains_inclusion::{Pallet, Call, Storage, Event<T>} = 53,
ParasInherent: parachains_paras_inherent::{Pallet, Call, Storage, Inherent} = 54, ParaInherent: parachains_paras_inherent::{Pallet, Call, Storage, Inherent} = 54,
ParaScheduler: parachains_scheduler::{Pallet, Storage} = 55, ParaScheduler: parachains_scheduler::{Pallet, Storage} = 55,
Paras: parachains_paras::{Pallet, Call, Storage, Event, Config} = 56, Paras: parachains_paras::{Pallet, Call, Storage, Event, Config} = 56,
Initializer: parachains_initializer::{Pallet, Call, Storage} = 57, Initializer: parachains_initializer::{Pallet, Call, Storage} = 57,
@@ -1150,6 +1150,9 @@ mod tests {
while System::block_number() < to { while System::block_number() < to {
let b = System::block_number(); let b = System::block_number();
if b != 0 { if b != 0 {
// circumvent requirement to have bitfields and headers in block for testing purposes
crate::paras_inherent::Included::<Test>::set(Some(()));
AllPallets::on_finalize(b); AllPallets::on_finalize(b);
System::finalize(); System::finalize();
} }
+3 -2
View File
@@ -17,8 +17,8 @@
//! Mocks for all the traits. //! Mocks for all the traits.
use crate::{ use crate::{
configuration, disputes, dmp, hrmp, inclusion, initializer, paras, scheduler, session_info, configuration, disputes, dmp, hrmp, inclusion, initializer, paras, paras_inherent, scheduler,
shared, ump, session_info, shared, ump,
}; };
use frame_support::{parameter_types, traits::GenesisBuild}; use frame_support::{parameter_types, traits::GenesisBuild};
use frame_support_test::TestRandomness; use frame_support_test::TestRandomness;
@@ -45,6 +45,7 @@ frame_support::construct_runtime!(
Configuration: configuration::{Pallet, Call, Storage, Config<T>}, Configuration: configuration::{Pallet, Call, Storage, Config<T>},
ParasShared: shared::{Pallet, Call, Storage}, ParasShared: shared::{Pallet, Call, Storage},
ParaInclusion: inclusion::{Pallet, Call, Storage, Event<T>}, ParaInclusion: inclusion::{Pallet, Call, Storage, Event<T>},
ParaInherent: paras_inherent::{Pallet, Call, Storage},
Scheduler: scheduler::{Pallet, Storage}, Scheduler: scheduler::{Pallet, Storage},
Initializer: initializer::{Pallet, Call, Storage}, Initializer: initializer::{Pallet, Call, Storage},
Dmp: dmp::{Pallet, Call, Storage}, Dmp: dmp::{Pallet, Call, Storage},
+208 -205
View File
@@ -28,20 +28,18 @@ use crate::{
shared, ump, shared, ump,
}; };
use frame_support::{ use frame_support::{
decl_error, decl_module, decl_storage,
dispatch::DispatchResultWithPostInfo,
ensure,
inherent::{InherentData, InherentIdentifier, MakeFatalError, ProvideInherent}, inherent::{InherentData, InherentIdentifier, MakeFatalError, ProvideInherent},
traits::Get, pallet_prelude::*,
weights::{DispatchClass, Weight},
}; };
use frame_system::ensure_none; use frame_system::pallet_prelude::*;
use primitives::v1::{ use primitives::v1::{
BackedCandidate, InherentData as ParachainsInherentData, PARACHAINS_INHERENT_IDENTIFIER, BackedCandidate, InherentData as ParachainsInherentData, PARACHAINS_INHERENT_IDENTIFIER,
}; };
use sp_runtime::traits::Header as HeaderT; use sp_runtime::traits::Header as HeaderT;
use sp_std::prelude::*; use sp_std::prelude::*;
pub use pallet::*;
const LOG_TARGET: &str = "runtime::inclusion-inherent"; const LOG_TARGET: &str = "runtime::inclusion-inherent";
// In the future, we should benchmark these consts; these are all untested assumptions for now. // In the future, we should benchmark these consts; these are all untested assumptions for now.
const BACKED_CANDIDATE_WEIGHT: Weight = 100_000; const BACKED_CANDIDATE_WEIGHT: Weight = 100_000;
@@ -49,22 +47,20 @@ const INCLUSION_INHERENT_CLAIMED_WEIGHT: Weight = 1_000_000_000;
// we assume that 75% of an paras inherent's weight is used processing backed candidates // we assume that 75% of an paras inherent's weight is used processing backed candidates
const MINIMAL_INCLUSION_INHERENT_WEIGHT: Weight = INCLUSION_INHERENT_CLAIMED_WEIGHT / 4; const MINIMAL_INCLUSION_INHERENT_WEIGHT: Weight = INCLUSION_INHERENT_CLAIMED_WEIGHT / 4;
#[frame_support::pallet]
pub mod pallet {
use super::*;
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
#[pallet::config]
#[pallet::disable_frame_system_supertrait_check]
pub trait Config: inclusion::Config + scheduler::Config {} pub trait Config: inclusion::Config + scheduler::Config {}
decl_storage! { #[pallet::error]
trait Store for Module<T: Config> as ParaInherent { pub enum Error<T> {
/// Whether the paras inherent was included within this block.
///
/// The `Option<()>` is effectively a `bool`, but it never hits storage in the `None` variant
/// due to the guarantees of FRAME's storage APIs.
///
/// If this is `None` at the end of the block, we panic and render the block invalid.
Included: Option<()>;
}
}
decl_error! {
pub enum Error for Module<T: Config> {
/// Inclusion inherent called more than once per block. /// Inclusion inherent called more than once per block.
TooManyInclusionInherents, TooManyInclusionInherents,
/// The hash of the submitted parent header doesn't correspond to the saved block hash of /// The hash of the submitted parent header doesn't correspond to the saved block hash of
@@ -73,201 +69,31 @@ decl_error! {
/// Potentially invalid candidate. /// Potentially invalid candidate.
CandidateCouldBeInvalid, CandidateCouldBeInvalid,
} }
}
decl_module! { /// Whether the paras inherent was included within this block.
/// The paras inherent module. ///
pub struct Module<T: Config> for enum Call where origin: <T as frame_system::Config>::Origin { /// The `Option<()>` is effectively a `bool`, but it never hits storage in the `None` variant
type Error = Error<T>; /// due to the guarantees of FRAME's storage APIs.
///
/// If this is `None` at the end of the block, we panic and render the block invalid.
#[pallet::storage]
pub(crate) type Included<T> = StorageValue<_, ()>;
fn on_initialize() -> Weight { #[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(_: T::BlockNumber) -> Weight {
T::DbWeight::get().reads_writes(1, 1) // in on_finalize. T::DbWeight::get().reads_writes(1, 1) // in on_finalize.
} }
fn on_finalize() { fn on_finalize(_: T::BlockNumber) {
if Included::take().is_none() { if Included::<T>::take().is_none() {
panic!("Bitfields and heads must be included every block"); panic!("Bitfields and heads must be included every block");
} }
} }
/// Enter the paras inherent. This will process bitfields and backed candidates.
#[weight = (
MINIMAL_INCLUSION_INHERENT_WEIGHT + data.backed_candidates.len() as Weight * BACKED_CANDIDATE_WEIGHT,
DispatchClass::Mandatory,
)]
pub fn enter(
origin,
data: ParachainsInherentData<T::Header>,
) -> DispatchResultWithPostInfo {
let ParachainsInherentData {
bitfields: signed_bitfields,
backed_candidates,
parent_header,
disputes,
} = data;
ensure_none(origin)?;
ensure!(!<Included>::exists(), Error::<T>::TooManyInclusionInherents);
// Check that the submitted parent header indeed corresponds to the previous block hash.
let parent_hash = <frame_system::Pallet<T>>::parent_hash();
ensure!(
parent_header.hash().as_ref() == parent_hash.as_ref(),
Error::<T>::InvalidParentHeader,
);
// Handle disputes logic.
let current_session = <shared::Pallet<T>>::session_index();
let freed_disputed: Vec<(_, FreedReason)> = {
let fresh_disputes = T::DisputesHandler::provide_multi_dispute_data(disputes)?;
if T::DisputesHandler::is_frozen() {
// The relay chain we are currently on is invalid. Proceed no further on parachains.
Included::set(Some(()));
return Ok(Some(
MINIMAL_INCLUSION_INHERENT_WEIGHT
).into());
} }
let any_current_session_disputes = fresh_disputes.iter() #[pallet::inherent]
.any(|(s, _)| s == &current_session); impl<T: Config> ProvideInherent for Pallet<T> {
if any_current_session_disputes {
let current_session_disputes: Vec<_> = fresh_disputes.iter()
.filter(|(s, _)| s == &current_session)
.map(|(_, c)| *c)
.collect();
<inclusion::Pallet<T>>::collect_disputed(current_session_disputes)
.into_iter()
.map(|core| (core, FreedReason::Concluded))
.collect()
} else {
Vec::new()
}
};
// Process new availability bitfields, yielding any availability cores whose
// work has now concluded.
let expected_bits = <scheduler::Pallet<T>>::availability_cores().len();
let freed_concluded = <inclusion::Pallet<T>>::process_bitfields(
expected_bits,
signed_bitfields,
<scheduler::Pallet<T>>::core_para,
)?;
// Inform the disputes module of all included candidates.
let now = <frame_system::Pallet<T>>::block_number();
for (_, candidate_hash) in &freed_concluded {
T::DisputesHandler::note_included(current_session, *candidate_hash, now);
}
// Handle timeouts for any availability core work.
let availability_pred = <scheduler::Pallet<T>>::availability_timeout_predicate();
let freed_timeout = if let Some(pred) = availability_pred {
<inclusion::Pallet<T>>::collect_pending(pred)
} else {
Vec::new()
};
// Schedule paras again, given freed cores, and reasons for freeing.
let mut freed = freed_disputed.into_iter()
.chain(freed_concluded.into_iter().map(|(c, _hash)| (c, FreedReason::Concluded)))
.chain(freed_timeout.into_iter().map(|c| (c, FreedReason::TimedOut)))
.collect::<Vec<_>>();
freed.sort_unstable_by_key(|pair| pair.0); // sort by core index
<scheduler::Pallet<T>>::clear();
<scheduler::Pallet<T>>::schedule(
freed,
<frame_system::Pallet<T>>::block_number(),
);
let backed_candidates = limit_backed_candidates::<T>(backed_candidates);
let backed_candidates_len = backed_candidates.len() as Weight;
// Refuse to back any candidates that are disputed or invalid.
for candidate in &backed_candidates {
ensure!(
!T::DisputesHandler::could_be_invalid(
current_session,
candidate.candidate.hash(),
),
Error::<T>::CandidateCouldBeInvalid,
);
}
// Process backed candidates according to scheduled cores.
let parent_storage_root = parent_header.state_root().clone();
let occupied = <inclusion::Pallet<T>>::process_candidates(
parent_storage_root,
backed_candidates,
<scheduler::Pallet<T>>::scheduled(),
<scheduler::Pallet<T>>::group_validators,
)?;
// Note which of the scheduled cores were actually occupied by a backed candidate.
<scheduler::Pallet<T>>::occupied(&occupied);
// Give some time slice to dispatch pending upward messages.
<ump::Pallet<T>>::process_pending_upward_messages();
// And track that we've finished processing the inherent for this block.
Included::set(Some(()));
Ok(Some(
MINIMAL_INCLUSION_INHERENT_WEIGHT +
(backed_candidates_len * BACKED_CANDIDATE_WEIGHT)
).into())
}
}
}
/// Limit the number of backed candidates processed in order to stay within block weight limits.
///
/// Use a configured assumption about the weight required to process a backed candidate and the
/// current block weight as of the execution of this function to ensure that we don't overload
/// the block with candidate processing.
///
/// If the backed candidates exceed the available block weight remaining, then skips all of them.
/// This is somewhat less desirable than attempting to fit some of them, but is more fair in the
/// even that we can't trust the provisioner to provide a fair / random ordering of candidates.
fn limit_backed_candidates<T: Config>(
mut backed_candidates: Vec<BackedCandidate<T::Hash>>,
) -> Vec<BackedCandidate<T::Hash>> {
const MAX_CODE_UPGRADES: usize = 1;
// Ignore any candidates beyond one that contain code upgrades.
//
// This is an artificial limitation that does not appear in the guide as it is a practical
// concern around execution.
{
let mut code_upgrades = 0;
backed_candidates.retain(|c| {
if c.candidate.commitments.new_validation_code.is_some() {
if code_upgrades >= MAX_CODE_UPGRADES {
return false
}
code_upgrades += 1;
}
true
});
}
// the weight of the paras inherent is already included in the current block weight,
// so our operation is simple: if the block is currently overloaded, make this intrinsic smaller
if frame_system::Pallet::<T>::block_weight().total() >
<T as frame_system::Config>::BlockWeights::get().max_block
{
Vec::new()
} else {
backed_candidates
}
}
impl<T: Config> ProvideInherent for Module<T> {
type Call = Call<T>; type Call = Call<T>;
type Error = MakeFatalError<()>; type Error = MakeFatalError<()>;
const INHERENT_IDENTIFIER: InherentIdentifier = PARACHAINS_INHERENT_IDENTIFIER; const INHERENT_IDENTIFIER: InherentIdentifier = PARACHAINS_INHERENT_IDENTIFIER;
@@ -317,6 +143,183 @@ impl<T: Config> ProvideInherent for Module<T> {
} }
} }
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Enter the paras inherent. This will process bitfields and backed candidates.
#[pallet::weight((
MINIMAL_INCLUSION_INHERENT_WEIGHT + data.backed_candidates.len() as Weight * BACKED_CANDIDATE_WEIGHT,
DispatchClass::Mandatory,
))]
pub fn enter(
origin: OriginFor<T>,
data: ParachainsInherentData<T::Header>,
) -> DispatchResultWithPostInfo {
let ParachainsInherentData {
bitfields: signed_bitfields,
backed_candidates,
parent_header,
disputes,
} = data;
ensure_none(origin)?;
ensure!(!Included::<T>::exists(), Error::<T>::TooManyInclusionInherents);
// Check that the submitted parent header indeed corresponds to the previous block hash.
let parent_hash = <frame_system::Pallet<T>>::parent_hash();
ensure!(
parent_header.hash().as_ref() == parent_hash.as_ref(),
Error::<T>::InvalidParentHeader,
);
// Handle disputes logic.
let current_session = <shared::Pallet<T>>::session_index();
let freed_disputed: Vec<(_, FreedReason)> = {
let fresh_disputes = T::DisputesHandler::provide_multi_dispute_data(disputes)?;
if T::DisputesHandler::is_frozen() {
// The relay chain we are currently on is invalid. Proceed no further on parachains.
Included::<T>::set(Some(()));
return Ok(Some(MINIMAL_INCLUSION_INHERENT_WEIGHT).into())
}
let any_current_session_disputes =
fresh_disputes.iter().any(|(s, _)| s == &current_session);
if any_current_session_disputes {
let current_session_disputes: Vec<_> = fresh_disputes
.iter()
.filter(|(s, _)| s == &current_session)
.map(|(_, c)| *c)
.collect();
<inclusion::Pallet<T>>::collect_disputed(current_session_disputes)
.into_iter()
.map(|core| (core, FreedReason::Concluded))
.collect()
} else {
Vec::new()
}
};
// Process new availability bitfields, yielding any availability cores whose
// work has now concluded.
let expected_bits = <scheduler::Pallet<T>>::availability_cores().len();
let freed_concluded = <inclusion::Pallet<T>>::process_bitfields(
expected_bits,
signed_bitfields,
<scheduler::Pallet<T>>::core_para,
)?;
// Inform the disputes module of all included candidates.
let now = <frame_system::Pallet<T>>::block_number();
for (_, candidate_hash) in &freed_concluded {
T::DisputesHandler::note_included(current_session, *candidate_hash, now);
}
// Handle timeouts for any availability core work.
let availability_pred = <scheduler::Pallet<T>>::availability_timeout_predicate();
let freed_timeout = if let Some(pred) = availability_pred {
<inclusion::Pallet<T>>::collect_pending(pred)
} else {
Vec::new()
};
// Schedule paras again, given freed cores, and reasons for freeing.
let mut freed = freed_disputed
.into_iter()
.chain(freed_concluded.into_iter().map(|(c, _hash)| (c, FreedReason::Concluded)))
.chain(freed_timeout.into_iter().map(|c| (c, FreedReason::TimedOut)))
.collect::<Vec<_>>();
freed.sort_unstable_by_key(|pair| pair.0); // sort by core index
<scheduler::Pallet<T>>::clear();
<scheduler::Pallet<T>>::schedule(freed, <frame_system::Pallet<T>>::block_number());
let backed_candidates = limit_backed_candidates::<T>(backed_candidates);
let backed_candidates_len = backed_candidates.len() as Weight;
// Refuse to back any candidates that are disputed or invalid.
for candidate in &backed_candidates {
ensure!(
!T::DisputesHandler::could_be_invalid(
current_session,
candidate.candidate.hash(),
),
Error::<T>::CandidateCouldBeInvalid,
);
}
// Process backed candidates according to scheduled cores.
let parent_storage_root = parent_header.state_root().clone();
let occupied = <inclusion::Pallet<T>>::process_candidates(
parent_storage_root,
backed_candidates,
<scheduler::Pallet<T>>::scheduled(),
<scheduler::Pallet<T>>::group_validators,
)?;
// Note which of the scheduled cores were actually occupied by a backed candidate.
<scheduler::Pallet<T>>::occupied(&occupied);
// Give some time slice to dispatch pending upward messages.
<ump::Pallet<T>>::process_pending_upward_messages();
// And track that we've finished processing the inherent for this block.
Included::<T>::set(Some(()));
Ok(Some(
MINIMAL_INCLUSION_INHERENT_WEIGHT +
(backed_candidates_len * BACKED_CANDIDATE_WEIGHT),
)
.into())
}
}
}
/// Limit the number of backed candidates processed in order to stay within block weight limits.
///
/// Use a configured assumption about the weight required to process a backed candidate and the
/// current block weight as of the execution of this function to ensure that we don't overload
/// the block with candidate processing.
///
/// If the backed candidates exceed the available block weight remaining, then skips all of them.
/// This is somewhat less desirable than attempting to fit some of them, but is more fair in the
/// even that we can't trust the provisioner to provide a fair / random ordering of candidates.
fn limit_backed_candidates<T: Config>(
mut backed_candidates: Vec<BackedCandidate<T::Hash>>,
) -> Vec<BackedCandidate<T::Hash>> {
const MAX_CODE_UPGRADES: usize = 1;
// Ignore any candidates beyond one that contain code upgrades.
//
// This is an artificial limitation that does not appear in the guide as it is a practical
// concern around execution.
{
let mut code_upgrades = 0;
backed_candidates.retain(|c| {
if c.candidate.commitments.new_validation_code.is_some() {
if code_upgrades >= MAX_CODE_UPGRADES {
return false
}
code_upgrades += 1;
}
true
});
}
// the weight of the paras inherent is already included in the current block weight,
// so our operation is simple: if the block is currently overloaded, make this intrinsic smaller
if frame_system::Pallet::<T>::block_weight().total() >
<T as frame_system::Config>::BlockWeights::get().max_block
{
Vec::new()
} else {
backed_candidates
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
+1 -1
View File
@@ -213,7 +213,7 @@ construct_runtime! {
Configuration: parachains_configuration::{Pallet, Call, Storage, Config<T>}, Configuration: parachains_configuration::{Pallet, Call, Storage, Config<T>},
ParasShared: parachains_shared::{Pallet, Call, Storage}, ParasShared: parachains_shared::{Pallet, Call, Storage},
ParaInclusion: parachains_inclusion::{Pallet, Call, Storage, Event<T>}, ParaInclusion: parachains_inclusion::{Pallet, Call, Storage, Event<T>},
ParasInherent: parachains_paras_inherent::{Pallet, Call, Storage, Inherent}, ParaInherent: parachains_paras_inherent::{Pallet, Call, Storage, Inherent},
ParaScheduler: parachains_scheduler::{Pallet, Storage}, ParaScheduler: parachains_scheduler::{Pallet, Storage},
Paras: parachains_paras::{Pallet, Call, Storage, Event, Config}, Paras: parachains_paras::{Pallet, Call, Storage, Event, Config},
Initializer: parachains_initializer::{Pallet, Call, Storage}, Initializer: parachains_initializer::{Pallet, Call, Storage},
+1 -1
View File
@@ -531,7 +531,7 @@ construct_runtime! {
// Parachains runtime modules // Parachains runtime modules
Configuration: parachains_configuration::{Pallet, Call, Storage, Config<T>}, Configuration: parachains_configuration::{Pallet, Call, Storage, Config<T>},
ParaInclusion: parachains_inclusion::{Pallet, Call, Storage, Event<T>}, ParaInclusion: parachains_inclusion::{Pallet, Call, Storage, Event<T>},
ParasInherent: parachains_paras_inherent::{Pallet, Call, Storage, Inherent}, ParaInherent: parachains_paras_inherent::{Pallet, Call, Storage, Inherent},
Initializer: parachains_initializer::{Pallet, Call, Storage}, Initializer: parachains_initializer::{Pallet, Call, Storage},
Paras: parachains_paras::{Pallet, Call, Storage, Origin, Event}, Paras: parachains_paras::{Pallet, Call, Storage, Origin, Event},
ParasShared: parachains_shared::{Pallet, Call, Storage}, ParasShared: parachains_shared::{Pallet, Call, Storage},
+1 -1
View File
@@ -1071,7 +1071,7 @@ construct_runtime! {
Configuration: parachains_configuration::{Pallet, Call, Storage, Config<T>} = 42, Configuration: parachains_configuration::{Pallet, Call, Storage, Config<T>} = 42,
ParasShared: parachains_shared::{Pallet, Call, Storage} = 43, ParasShared: parachains_shared::{Pallet, Call, Storage} = 43,
ParaInclusion: parachains_inclusion::{Pallet, Call, Storage, Event<T>} = 44, ParaInclusion: parachains_inclusion::{Pallet, Call, Storage, Event<T>} = 44,
ParasInherent: parachains_paras_inherent::{Pallet, Call, Storage, Inherent} = 45, ParaInherent: parachains_paras_inherent::{Pallet, Call, Storage, Inherent} = 45,
ParaScheduler: parachains_scheduler::{Pallet, Storage} = 46, ParaScheduler: parachains_scheduler::{Pallet, Storage} = 46,
Paras: parachains_paras::{Pallet, Call, Storage, Event, Config} = 47, Paras: parachains_paras::{Pallet, Call, Storage, Event, Config} = 47,
Initializer: parachains_initializer::{Pallet, Call, Storage} = 48, Initializer: parachains_initializer::{Pallet, Call, Storage} = 48,