#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(doc)]
#[cfg_attr(doc, aquamarine::aquamarine)]
pub mod block_flowchart {}
#[cfg(test)]
mod tests;
extern crate alloc;
use codec::{Codec, Encode};
use core::marker::PhantomData;
use frame_support::{
defensive_assert,
dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo},
migrations::MultiStepMigrator,
pallet_prelude::InvalidTransaction,
traits::{
BeforeAllRuntimeMigrations, EnsureInherentsAreFirst, ExecuteBlock, OffchainWorker,
OnFinalize, OnIdle, OnInitialize, OnPoll, OnRuntimeUpgrade, PostInherents,
PostTransactions, PreInherents,
},
weights::{Weight, WeightMeter},
};
use frame_system::pallet_prelude::BlockNumberFor;
use sp_runtime::{
generic::Digest,
traits::{
self, Applyable, CheckEqual, Checkable, Dispatchable, Header, NumberFor, One,
ValidateUnsigned, Zero,
},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, ExtrinsicInclusionMode,
};
#[cfg(feature = "try-runtime")]
use ::{
frame_support::{
traits::{TryDecodeEntireStorage, TryDecodeEntireStorageError, TryState},
StorageNoopGuard,
},
frame_try_runtime::{TryStateSelect, UpgradeCheckSelect},
log,
sp_runtime::TryRuntimeError,
};
#[allow(dead_code)]
const LOG_TARGET: &str = "runtime::executive";
pub type CheckedOf<E, C> = <E as Checkable<C>>::Checked;
pub type CallOf<E, C> = <CheckedOf<E, C> as Applyable>::Call;
pub type OriginOf<E, C> = <CallOf<E, C> as Dispatchable>::RuntimeOrigin;
pub struct Executive<
System,
Block,
Context,
UnsignedValidator,
AllPalletsWithSystem,
OnRuntimeUpgrade = (),
>(
PhantomData<(
System,
Block,
Context,
UnsignedValidator,
AllPalletsWithSystem,
OnRuntimeUpgrade,
)>,
);
impl<
System: frame_system::Config + EnsureInherentsAreFirst<Block>,
Block: traits::Block<
Header = frame_system::pallet_prelude::HeaderFor<System>,
Hash = System::Hash,
>,
Context: Default,
UnsignedValidator,
AllPalletsWithSystem: OnRuntimeUpgrade
+ BeforeAllRuntimeMigrations
+ OnInitialize<BlockNumberFor<System>>
+ OnIdle<BlockNumberFor<System>>
+ OnFinalize<BlockNumberFor<System>>
+ OffchainWorker<BlockNumberFor<System>>
+ OnPoll<BlockNumberFor<System>>,
COnRuntimeUpgrade: OnRuntimeUpgrade,
> ExecuteBlock<Block>
for Executive<System, Block, Context, UnsignedValidator, AllPalletsWithSystem, COnRuntimeUpgrade>
where
Block::Extrinsic: Checkable<Context> + Codec,
CheckedOf<Block::Extrinsic, Context>: Applyable + GetDispatchInfo,
CallOf<Block::Extrinsic, Context>:
Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
OriginOf<Block::Extrinsic, Context>: From<Option<System::AccountId>>,
UnsignedValidator: ValidateUnsigned<Call = CallOf<Block::Extrinsic, Context>>,
{
fn execute_block(block: Block) {
Executive::<
System,
Block,
Context,
UnsignedValidator,
AllPalletsWithSystem,
COnRuntimeUpgrade,
>::execute_block(block);
}
}
#[cfg(feature = "try-runtime")]
impl<
System: frame_system::Config + EnsureInherentsAreFirst<Block>,
Block: traits::Block<
Header = frame_system::pallet_prelude::HeaderFor<System>,
Hash = System::Hash,
>,
Context: Default,
UnsignedValidator,
AllPalletsWithSystem: OnRuntimeUpgrade
+ BeforeAllRuntimeMigrations
+ OnInitialize<BlockNumberFor<System>>
+ OnIdle<BlockNumberFor<System>>
+ OnFinalize<BlockNumberFor<System>>
+ OffchainWorker<BlockNumberFor<System>>
+ OnPoll<BlockNumberFor<System>>
+ TryState<BlockNumberFor<System>>
+ TryDecodeEntireStorage,
COnRuntimeUpgrade: OnRuntimeUpgrade,
> Executive<System, Block, Context, UnsignedValidator, AllPalletsWithSystem, COnRuntimeUpgrade>
where
Block::Extrinsic: Checkable<Context> + Codec,
CheckedOf<Block::Extrinsic, Context>: Applyable + GetDispatchInfo,
CallOf<Block::Extrinsic, Context>:
Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
OriginOf<Block::Extrinsic, Context>: From<Option<System::AccountId>>,
UnsignedValidator: ValidateUnsigned<Call = CallOf<Block::Extrinsic, Context>>,
{
pub fn try_execute_block(
block: Block,
state_root_check: bool,
signature_check: bool,
select: frame_try_runtime::TryStateSelect,
) -> Result<Weight, &'static str> {
log::info!(
target: LOG_TARGET,
"try-runtime: executing block #{:?} / state root check: {:?} / signature check: {:?} / try-state-select: {:?}",
block.header().number(),
state_root_check,
signature_check,
select,
);
let mode = Self::initialize_block(block.header());
let num_inherents = Self::initial_checks(&block) as usize;
let (header, extrinsics) = block.deconstruct();
if mode == ExtrinsicInclusionMode::OnlyInherents && extrinsics.len() > num_inherents {
return Err("Only inherents allowed".into())
}
let try_apply_extrinsic = |uxt: Block::Extrinsic| -> ApplyExtrinsicResult {
sp_io::init_tracing();
let encoded = uxt.encode();
let encoded_len = encoded.len();
let is_inherent = System::is_inherent(&uxt);
let xt = if signature_check {
uxt.check(&Default::default())
} else {
uxt.unchecked_into_checked_i_know_what_i_am_doing(&Default::default())
}?;
let dispatch_info = xt.get_dispatch_info();
if !is_inherent && !<frame_system::Pallet<System>>::inherents_applied() {
Self::inherents_applied();
}
<frame_system::Pallet<System>>::note_extrinsic(encoded);
let r = Applyable::apply::<UnsignedValidator>(xt, &dispatch_info, encoded_len)?;
if r.is_err() && dispatch_info.class == DispatchClass::Mandatory {
return Err(InvalidTransaction::BadMandatory.into())
}
<frame_system::Pallet<System>>::note_applied_extrinsic(&r, dispatch_info);
Ok(r.map(|_| ()).map_err(|e| e.error))
};
for e in extrinsics.iter() {
if let Err(err) = try_apply_extrinsic(e.clone()) {
log::error!(
target: LOG_TARGET, "transaction {:?} failed due to {:?}. Aborting the rest of the block execution.",
e,
err,
);
break
}
}
if !<frame_system::Pallet<System>>::inherents_applied() {
Self::inherents_applied();
}
<frame_system::Pallet<System>>::note_finished_extrinsics();
<System as frame_system::Config>::PostTransactions::post_transactions();
Self::on_idle_hook(*header.number());
Self::on_finalize_hook(*header.number());
let _guard = frame_support::StorageNoopGuard::default();
<AllPalletsWithSystem as frame_support::traits::TryState<
BlockNumberFor<System>,
>>::try_state(*header.number(), select.clone())
.inspect_err(|e| {
log::error!(target: LOG_TARGET, "failure: {:?}", e);
})?;
if select.any() {
let res = AllPalletsWithSystem::try_decode_entire_state();
Self::log_decode_result(res)?;
}
drop(_guard);
{
let new_header = <frame_system::Pallet<System>>::finalize();
let items_zip = header.digest().logs().iter().zip(new_header.digest().logs().iter());
for (header_item, computed_item) in items_zip {
header_item.check_equal(computed_item);
assert!(header_item == computed_item, "Digest item must match that calculated.");
}
if state_root_check {
let storage_root = new_header.state_root();
header.state_root().check_equal(storage_root);
assert!(
header.state_root() == storage_root,
"Storage root must match that calculated."
);
}
assert!(
header.extrinsics_root() == new_header.extrinsics_root(),
"Transaction trie root must be valid.",
);
}
log::info!(
target: LOG_TARGET,
"try-runtime: Block #{:?} successfully executed",
header.number(),
);
Ok(frame_system::Pallet::<System>::block_weight().total())
}
pub fn try_runtime_upgrade(checks: UpgradeCheckSelect) -> Result<Weight, TryRuntimeError> {
let before_all_weight =
<AllPalletsWithSystem as BeforeAllRuntimeMigrations>::before_all_runtime_migrations();
let try_on_runtime_upgrade_weight =
<(COnRuntimeUpgrade, AllPalletsWithSystem) as OnRuntimeUpgrade>::try_on_runtime_upgrade(
checks.pre_and_post(),
)?;
frame_system::LastRuntimeUpgrade::<System>::put(
frame_system::LastRuntimeUpgradeInfo::from(
<System::Version as frame_support::traits::Get<_>>::get(),
),
);
let _guard = StorageNoopGuard::default();
if checks.any() {
let res = AllPalletsWithSystem::try_decode_entire_state();
Self::log_decode_result(res)?;
}
if checks.try_state() {
AllPalletsWithSystem::try_state(
frame_system::Pallet::<System>::block_number(),
TryStateSelect::All,
)?;
}
Ok(before_all_weight.saturating_add(try_on_runtime_upgrade_weight))
}
fn log_decode_result(
res: Result<usize, alloc::vec::Vec<TryDecodeEntireStorageError>>,
) -> Result<(), TryRuntimeError> {
match res {
Ok(bytes) => {
log::info!(
target: LOG_TARGET,
"✅ Entire runtime state decodes without error. {} bytes total.",
bytes
);
Ok(())
},
Err(errors) => {
log::error!(
target: LOG_TARGET,
"`try_decode_entire_state` failed with {} errors",
errors.len(),
);
for (i, err) in errors.iter().enumerate() {
log::error!(target: LOG_TARGET, "- {i}. error: {err}");
log::debug!(target: LOG_TARGET, "- {i}. error: {err:?}");
}
Err("`try_decode_entire_state` failed".into())
},
}
}
}
impl<
System: frame_system::Config + EnsureInherentsAreFirst<Block>,
Block: traits::Block<
Header = frame_system::pallet_prelude::HeaderFor<System>,
Hash = System::Hash,
>,
Context: Default,
UnsignedValidator,
AllPalletsWithSystem: OnRuntimeUpgrade
+ BeforeAllRuntimeMigrations
+ OnInitialize<BlockNumberFor<System>>
+ OnIdle<BlockNumberFor<System>>
+ OnFinalize<BlockNumberFor<System>>
+ OffchainWorker<BlockNumberFor<System>>
+ OnPoll<BlockNumberFor<System>>,
COnRuntimeUpgrade: OnRuntimeUpgrade,
> Executive<System, Block, Context, UnsignedValidator, AllPalletsWithSystem, COnRuntimeUpgrade>
where
Block::Extrinsic: Checkable<Context> + Codec,
CheckedOf<Block::Extrinsic, Context>: Applyable + GetDispatchInfo,
CallOf<Block::Extrinsic, Context>:
Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
OriginOf<Block::Extrinsic, Context>: From<Option<System::AccountId>>,
UnsignedValidator: ValidateUnsigned<Call = CallOf<Block::Extrinsic, Context>>,
{
pub fn execute_on_runtime_upgrade() -> Weight {
let before_all_weight =
<AllPalletsWithSystem as BeforeAllRuntimeMigrations>::before_all_runtime_migrations();
let runtime_upgrade_weight = <(
COnRuntimeUpgrade,
<System as frame_system::Config>::SingleBlockMigrations,
AllPalletsWithSystem,
) as OnRuntimeUpgrade>::on_runtime_upgrade();
before_all_weight.saturating_add(runtime_upgrade_weight)
}
pub fn initialize_block(
header: &frame_system::pallet_prelude::HeaderFor<System>,
) -> ExtrinsicInclusionMode {
sp_io::init_tracing();
sp_tracing::enter_span!(sp_tracing::Level::TRACE, "init_block");
let digests = Self::extract_pre_digest(header);
Self::initialize_block_impl(header.number(), header.parent_hash(), &digests);
Self::extrinsic_mode()
}
fn extrinsic_mode() -> ExtrinsicInclusionMode {
if <System as frame_system::Config>::MultiBlockMigrator::ongoing() {
ExtrinsicInclusionMode::OnlyInherents
} else {
ExtrinsicInclusionMode::AllExtrinsics
}
}
fn extract_pre_digest(header: &frame_system::pallet_prelude::HeaderFor<System>) -> Digest {
let mut digest = <Digest>::default();
header.digest().logs().iter().for_each(|d| {
if d.as_pre_runtime().is_some() {
digest.push(d.clone())
}
});
digest
}
fn initialize_block_impl(
block_number: &BlockNumberFor<System>,
parent_hash: &System::Hash,
digest: &Digest,
) {
<frame_system::Pallet<System>>::reset_events();
let mut weight = Weight::zero();
if Self::runtime_upgraded() {
weight = weight.saturating_add(Self::execute_on_runtime_upgrade());
frame_system::LastRuntimeUpgrade::<System>::put(
frame_system::LastRuntimeUpgradeInfo::from(
<System::Version as frame_support::traits::Get<_>>::get(),
),
);
}
<frame_system::Pallet<System>>::initialize(block_number, parent_hash, digest);
weight = weight.saturating_add(<AllPalletsWithSystem as OnInitialize<
BlockNumberFor<System>,
>>::on_initialize(*block_number));
weight = weight.saturating_add(
<System::BlockWeights as frame_support::traits::Get<_>>::get().base_block,
);
<frame_system::Pallet<System>>::register_extra_weight_unchecked(
weight,
DispatchClass::Mandatory,
);
frame_system::Pallet::<System>::note_finished_initialize();
<System as frame_system::Config>::PreInherents::pre_inherents();
}
fn runtime_upgraded() -> bool {
let last = frame_system::LastRuntimeUpgrade::<System>::get();
let current = <System::Version as frame_support::traits::Get<_>>::get();
last.map(|v| v.was_upgraded(¤t)).unwrap_or(true)
}
fn initial_checks(block: &Block) -> u32 {
sp_tracing::enter_span!(sp_tracing::Level::TRACE, "initial_checks");
let header = block.header();
let n = *header.number();
assert!(
n > BlockNumberFor::<System>::zero() &&
<frame_system::Pallet<System>>::block_hash(n - BlockNumberFor::<System>::one()) ==
*header.parent_hash(),
"Parent hash should be valid.",
);
match System::ensure_inherents_are_first(block) {
Ok(num) => num,
Err(i) => panic!("Invalid inherent position for extrinsic at index {}", i),
}
}
pub fn execute_block(block: Block) {
sp_io::init_tracing();
sp_tracing::within_span! {
sp_tracing::info_span!("execute_block", ?block);
let mode = Self::initialize_block(block.header());
let num_inherents = Self::initial_checks(&block) as usize;
let (header, extrinsics) = block.deconstruct();
let num_extrinsics = extrinsics.len();
if mode == ExtrinsicInclusionMode::OnlyInherents && num_extrinsics > num_inherents {
panic!("Only inherents are allowed in this block")
}
Self::apply_extrinsics(extrinsics.into_iter());
if !<frame_system::Pallet<System>>::inherents_applied() {
defensive_assert!(num_inherents == num_extrinsics);
Self::inherents_applied();
}
<frame_system::Pallet<System>>::note_finished_extrinsics();
<System as frame_system::Config>::PostTransactions::post_transactions();
Self::on_idle_hook(*header.number());
Self::on_finalize_hook(*header.number());
Self::final_checks(&header);
}
}
pub fn inherents_applied() {
<frame_system::Pallet<System>>::note_inherents_applied();
<System as frame_system::Config>::PostInherents::post_inherents();
if <System as frame_system::Config>::MultiBlockMigrator::ongoing() {
let used_weight = <System as frame_system::Config>::MultiBlockMigrator::step();
<frame_system::Pallet<System>>::register_extra_weight_unchecked(
used_weight,
DispatchClass::Mandatory,
);
} else {
let block_number = <frame_system::Pallet<System>>::block_number();
Self::on_poll_hook(block_number);
}
}
fn apply_extrinsics(extrinsics: impl Iterator<Item = Block::Extrinsic>) {
extrinsics.into_iter().for_each(|e| {
if let Err(e) = Self::apply_extrinsic(e) {
let err: &'static str = e.into();
panic!("{}", err)
}
});
}
pub fn finalize_block() -> frame_system::pallet_prelude::HeaderFor<System> {
sp_io::init_tracing();
sp_tracing::enter_span!(sp_tracing::Level::TRACE, "finalize_block");
if !<frame_system::Pallet<System>>::inherents_applied() {
Self::inherents_applied();
}
<frame_system::Pallet<System>>::note_finished_extrinsics();
<System as frame_system::Config>::PostTransactions::post_transactions();
let block_number = <frame_system::Pallet<System>>::block_number();
Self::on_idle_hook(block_number);
Self::on_finalize_hook(block_number);
<frame_system::Pallet<System>>::finalize()
}
fn on_idle_hook(block_number: NumberFor<Block>) {
if <System as frame_system::Config>::MultiBlockMigrator::ongoing() {
return
}
let weight = <frame_system::Pallet<System>>::block_weight();
let max_weight = <System::BlockWeights as frame_support::traits::Get<_>>::get().max_block;
let remaining_weight = max_weight.saturating_sub(weight.total());
if remaining_weight.all_gt(Weight::zero()) {
let used_weight = <AllPalletsWithSystem as OnIdle<BlockNumberFor<System>>>::on_idle(
block_number,
remaining_weight,
);
<frame_system::Pallet<System>>::register_extra_weight_unchecked(
used_weight,
DispatchClass::Mandatory,
);
}
}
fn on_poll_hook(block_number: NumberFor<Block>) {
defensive_assert!(
!<System as frame_system::Config>::MultiBlockMigrator::ongoing(),
"on_poll should not be called during migrations"
);
let weight = <frame_system::Pallet<System>>::block_weight();
let max_weight = <System::BlockWeights as frame_support::traits::Get<_>>::get().max_block;
let remaining = max_weight.saturating_sub(weight.total());
if remaining.all_gt(Weight::zero()) {
let mut meter = WeightMeter::with_limit(remaining);
<AllPalletsWithSystem as OnPoll<BlockNumberFor<System>>>::on_poll(
block_number,
&mut meter,
);
<frame_system::Pallet<System>>::register_extra_weight_unchecked(
meter.consumed(),
DispatchClass::Mandatory,
);
}
}
fn on_finalize_hook(block_number: NumberFor<Block>) {
<AllPalletsWithSystem as OnFinalize<BlockNumberFor<System>>>::on_finalize(block_number);
}
pub fn apply_extrinsic(uxt: Block::Extrinsic) -> ApplyExtrinsicResult {
sp_io::init_tracing();
let encoded = uxt.encode();
let encoded_len = encoded.len();
sp_tracing::enter_span!(sp_tracing::info_span!("apply_extrinsic",
ext=?sp_core::hexdisplay::HexDisplay::from(&encoded)));
let is_inherent = System::is_inherent(&uxt);
let xt = uxt.check(&Default::default())?;
let dispatch_info = xt.get_dispatch_info();
if !is_inherent && !<frame_system::Pallet<System>>::inherents_applied() {
Self::inherents_applied();
}
<frame_system::Pallet<System>>::note_extrinsic(encoded);
let r = Applyable::apply::<UnsignedValidator>(xt, &dispatch_info, encoded_len)?;
if r.is_err() && dispatch_info.class == DispatchClass::Mandatory {
return Err(InvalidTransaction::BadMandatory.into())
}
<frame_system::Pallet<System>>::note_applied_extrinsic(&r, dispatch_info);
Ok(r.map(|_| ()).map_err(|e| e.error))
}
fn final_checks(header: &frame_system::pallet_prelude::HeaderFor<System>) {
sp_tracing::enter_span!(sp_tracing::Level::TRACE, "final_checks");
let new_header = <frame_system::Pallet<System>>::finalize();
assert_eq!(
header.digest().logs().len(),
new_header.digest().logs().len(),
"Number of digest items must match that calculated."
);
let items_zip = header.digest().logs().iter().zip(new_header.digest().logs().iter());
for (header_item, computed_item) in items_zip {
header_item.check_equal(computed_item);
assert!(header_item == computed_item, "Digest item must match that calculated.");
}
let storage_root = new_header.state_root();
header.state_root().check_equal(storage_root);
assert!(header.state_root() == storage_root, "Storage root must match that calculated.");
assert!(
header.extrinsics_root() == new_header.extrinsics_root(),
"Transaction trie root must be valid.",
);
}
pub fn validate_transaction(
source: TransactionSource,
uxt: Block::Extrinsic,
block_hash: Block::Hash,
) -> TransactionValidity {
sp_io::init_tracing();
use sp_tracing::{enter_span, within_span};
<frame_system::Pallet<System>>::initialize(
&(frame_system::Pallet::<System>::block_number() + One::one()),
&block_hash,
&Default::default(),
);
enter_span! { sp_tracing::Level::TRACE, "validate_transaction" };
let encoded_len = within_span! { sp_tracing::Level::TRACE, "using_encoded";
uxt.using_encoded(|d| d.len())
};
let xt = within_span! { sp_tracing::Level::TRACE, "check";
uxt.check(&Default::default())
}?;
let dispatch_info = within_span! { sp_tracing::Level::TRACE, "dispatch_info";
xt.get_dispatch_info()
};
if dispatch_info.class == DispatchClass::Mandatory {
return Err(InvalidTransaction::MandatoryValidation.into())
}
within_span! {
sp_tracing::Level::TRACE, "validate";
xt.validate::<UnsignedValidator>(source, &dispatch_info, encoded_len)
}
}
pub fn offchain_worker(header: &frame_system::pallet_prelude::HeaderFor<System>) {
sp_io::init_tracing();
let digests = header.digest().clone();
<frame_system::Pallet<System>>::initialize(header.number(), header.parent_hash(), &digests);
frame_system::BlockHash::<System>::insert(header.number(), header.hash());
<AllPalletsWithSystem as OffchainWorker<BlockNumberFor<System>>>::offchain_worker(
*header.number(),
)
}
}