Skip to main content

topsoil_executive/
lib.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later WITH Classpath-exception-2.0
6
7#![cfg_attr(not(feature = "std"), no_std)]
8
9//! # Executive Module
10//!
11//! The Executive module acts as the orchestration layer for the runtime. It dispatches incoming
12//! extrinsic calls to the respective modules in the runtime.
13//!
14//! ## Overview
15//!
16//! The executive module is not a typical pallet providing functionality around a specific feature.
17//! It is a cross-cutting framework component for the FRAME. It works in conjunction with the
18//! [FRAME System module](../topsoil_system/index.html) to perform these cross-cutting functions.
19//!
20//! The Executive module provides functions to:
21//!
22//! - Check transaction validity.
23//! - Initialize a block.
24//! - Apply extrinsics.
25//! - Execute a block.
26//! - Finalize a block.
27//! - Start an off-chain worker.
28//!
29//! The flow of their application in a block is explained in the [block flowchart](block_flowchart).
30//!
31//! ### Implementations
32//!
33//! The Executive module provides the following implementations:
34//!
35//! - `ExecuteBlock`: Trait that can be used to execute a block.
36//! - `Executive`: Type that can be used to make the FRAME available from the runtime.
37//!
38//! ## Usage
39//!
40//! The default Substrate node template declares the [`Executive`](./struct.Executive.html) type in
41//! its library.
42//!
43//! ### Example
44//!
45//! `Executive` type declaration from the node template.
46//!
47//! ```
48//! # use subsoil::runtime::generic;
49//! # use topsoil_executive as executive;
50//! # pub struct UncheckedExtrinsic {};
51//! # pub struct Header {};
52//! # type Context = topsoil_core::system::ChainContext<Runtime>;
53//! # pub type Block = generic::Block<Header, UncheckedExtrinsic>;
54//! # pub type Balances = u64;
55//! # pub type AllPalletsWithSystem = u64;
56//! # pub enum Runtime {};
57//! # use subsoil::runtime::transaction_validity::{
58//! #    TransactionValidity, UnknownTransaction, TransactionSource,
59//! # };
60//! # use subsoil::runtime::traits::ValidateUnsigned;
61//! # impl ValidateUnsigned for Runtime {
62//! #     type Call = ();
63//! #
64//! #     fn validate_unsigned(_source: TransactionSource, _call: &Self::Call) -> TransactionValidity {
65//! #         UnknownTransaction::NoUnsignedValidator.into()
66//! #     }
67//! # }
68//! /// Executive: handles dispatch to the various modules.
69//! pub type Executive = executive::Executive<Runtime, Block, Context, Runtime, AllPalletsWithSystem>;
70//! ```
71
72#[cfg(doc)]
73#[cfg_attr(doc, aquamarine::aquamarine)]
74/// # Block Execution
75///
76/// These are the steps of block execution as done by [`Executive::execute_block`]. A block is
77/// invalid if any of them fail.
78///
79/// ```mermaid
80/// flowchart TD
81///     Executive::execute_block --> on_runtime_upgrade
82///     on_runtime_upgrade --> System::initialize
83///     Executive::initialize_block --> System::initialize
84///     System::initialize --> on_initialize
85///     on_initialize --> PreInherents[System::PreInherents]
86///     PreInherents --> Inherents[Apply Inherents]
87///     Inherents --> PostInherents[System::PostInherents]
88///     PostInherents --> Check{MBM ongoing?}
89///     Check -->|No| poll
90///     Check -->|Yes| post_transactions_2[System::PostTransaction]
91///     post_transactions_2 --> Step[MBMs::step]
92///     Step --> on_finalize
93///     poll --> transactions[Apply Transactions]
94///     transactions --> post_transactions_1[System::PostTransaction]
95///     post_transactions_1 --> CheckIdle{Weight remaining?}
96///     CheckIdle -->|Yes| on_idle
97///     CheckIdle -->|No| on_finalize
98///     on_idle --> on_finalize
99/// ```
100pub mod block_flowchart {}
101
102#[cfg(test)]
103mod tests;
104
105extern crate alloc;
106
107use codec::{Codec, Encode};
108use core::marker::PhantomData;
109use subsoil::runtime::{
110	generic::Digest,
111	traits::{
112		self, Applyable, CheckEqual, Checkable, Dispatchable, Header, LazyBlock, NumberFor, One,
113		ValidateUnsigned, Zero,
114	},
115	transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError},
116	ApplyExtrinsicResult, ExtrinsicInclusionMode,
117};
118use topsoil_core::{
119	defensive_assert,
120	dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo},
121	migrations::MultiStepMigrator,
122	pallet_prelude::InvalidTransaction,
123	traits::{
124		BeforeAllRuntimeMigrations, ExecuteBlock, Get, IsInherent, OffchainWorker, OnFinalize,
125		OnIdle, OnInitialize, OnPoll, OnRuntimeUpgrade, PostInherents, PostTransactions,
126		PreInherents,
127	},
128	weights::{Weight, WeightMeter},
129	MAX_EXTRINSIC_DEPTH,
130};
131use topsoil_core::system::pallet_prelude::BlockNumberFor;
132
133#[cfg(feature = "try-runtime")]
134use ::{
135	log,
136	subsoil::runtime::TryRuntimeError,
137	topsoil_core::{
138		traits::{TryDecodeEntireStorage, TryDecodeEntireStorageError, TryState},
139		StorageNoopGuard,
140	},
141	topsoil_try_runtime::{TryStateSelect, UpgradeCheckSelect},
142};
143
144#[allow(dead_code)]
145const LOG_TARGET: &str = "runtime::executive";
146
147pub type CheckedOf<E, C> = <E as Checkable<C>>::Checked;
148pub type CallOf<E, C> = <CheckedOf<E, C> as Applyable>::Call;
149pub type OriginOf<E, C> = <CallOf<E, C> as Dispatchable>::RuntimeOrigin;
150
151/// Configuration for try-runtime upgrade checks.
152#[cfg(feature = "try-runtime")]
153#[derive(Debug, Clone)]
154pub struct TryRuntimeUpgradeConfig {
155	/// Whether to execute `pre/post_upgrade` and `try_state` hooks.
156	pub checks: UpgradeCheckSelect,
157	/// Which pallets' try_state hooks to execute.
158	pub try_state_select: TryStateSelect,
159}
160
161#[cfg(feature = "try-runtime")]
162impl TryRuntimeUpgradeConfig {
163	/// Create a new config with default settings (run all checks for all pallets).
164	pub fn new(checks: UpgradeCheckSelect) -> Self {
165		Self { checks, try_state_select: TryStateSelect::All }
166	}
167
168	/// Set which pallets' try_state hooks to execute.
169	pub fn with_try_state_select(mut self, try_state_select: TryStateSelect) -> Self {
170		self.try_state_select = try_state_select;
171		self
172	}
173}
174
175#[derive(PartialEq)]
176pub enum ExecutiveError {
177	UnableToDecodeExtrinsic,
178	InvalidInherentPosition(usize),
179	OnlyInherentsAllowed,
180	ApplyExtrinsic(TransactionValidityError),
181	Custom(&'static str),
182}
183
184impl core::fmt::Debug for ExecutiveError {
185	fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
186		match self {
187			ExecutiveError::UnableToDecodeExtrinsic => {
188				write!(fmt, "The extrinsic could not be decoded correctly")
189			},
190			ExecutiveError::InvalidInherentPosition(i) => {
191				write!(fmt, "Invalid inherent position for extrinsic at index {}", i)
192			},
193			ExecutiveError::OnlyInherentsAllowed => {
194				write!(fmt, "Only inherents are allowed in this block")
195			},
196			ExecutiveError::ApplyExtrinsic(e) => write!(
197				fmt,
198				"ExecuteBlockError applying extrinsic: {}",
199				Into::<&'static str>::into(*e)
200			),
201			ExecutiveError::Custom(err) => write!(fmt, "{err}"),
202		}
203	}
204}
205
206/// Main entry point for certain runtime actions as e.g. `execute_block`.
207///
208/// Generic parameters:
209/// - `System`: Something that implements `topsoil_core::system::Config`
210/// - `Block`: The block type of the runtime
211/// - `Context`: The context that is used when checking an extrinsic.
212/// - `UnsignedValidator`: The unsigned transaction validator of the runtime.
213/// - `AllPalletsWithSystem`: Tuple that contains all pallets including frame system pallet. Will be
214///   used to call hooks e.g. `on_initialize`.
215/// - [**DEPRECATED** `OnRuntimeUpgrade`]: This parameter is deprecated and will be removed after
216///   September 2026. Use type `SingleBlockMigrations` in topsoil_core::system::Config instead.
217#[allow(deprecated)]
218pub struct Executive<
219	System,
220	Block,
221	Context,
222	UnsignedValidator,
223	AllPalletsWithSystem,
224	OnRuntimeUpgrade = (),
225>(
226	PhantomData<(
227		System,
228		Block,
229		Context,
230		UnsignedValidator,
231		AllPalletsWithSystem,
232		OnRuntimeUpgrade,
233	)>,
234);
235
236/// TODO: The `OnRuntimeUpgrade` generic parameter in `Executive` is deprecated and will be
237/// removed in a future version. Once removed, this `#[allow(deprecated)]` attribute
238/// can be safely deleted.
239#[allow(deprecated)]
240impl<
241		System: topsoil_core::system::Config + IsInherent<Block::Extrinsic>,
242		Block: traits::Block<
243			Header = topsoil_core::system::pallet_prelude::HeaderFor<System>,
244			Hash = System::Hash,
245		>,
246		Context: Default,
247		UnsignedValidator,
248		AllPalletsWithSystem: OnRuntimeUpgrade
249			+ BeforeAllRuntimeMigrations
250			+ OnInitializeWithWeightRegistration<System>
251			+ OnIdle<BlockNumberFor<System>>
252			+ OnFinalize<BlockNumberFor<System>>
253			+ OffchainWorker<BlockNumberFor<System>>
254			+ OnPoll<BlockNumberFor<System>>,
255		COnRuntimeUpgrade: OnRuntimeUpgrade,
256	> ExecuteBlock<Block>
257	for Executive<System, Block, Context, UnsignedValidator, AllPalletsWithSystem, COnRuntimeUpgrade>
258where
259	Block::Extrinsic: Checkable<Context> + Codec,
260	CheckedOf<Block::Extrinsic, Context>: Applyable + GetDispatchInfo,
261	CallOf<Block::Extrinsic, Context>:
262		Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
263	OriginOf<Block::Extrinsic, Context>: From<Option<System::AccountId>>,
264	UnsignedValidator: ValidateUnsigned<Call = CallOf<Block::Extrinsic, Context>>,
265{
266	fn verify_and_remove_seal(_: &mut <Block as traits::Block>::LazyBlock) {
267		// Nothing to do here.
268	}
269
270	fn execute_verified_block(block: Block::LazyBlock) {
271		Executive::<
272			System,
273			Block,
274			Context,
275			UnsignedValidator,
276			AllPalletsWithSystem,
277			COnRuntimeUpgrade,
278		>::execute_block(block);
279	}
280}
281
282/// TODO: The `OnRuntimeUpgrade` generic parameter in `Executive` is deprecated and will be
283/// removed in a future version. Once removed, this `#[allow(deprecated)]` attribute
284/// can be safely deleted.
285#[allow(deprecated)]
286#[cfg(feature = "try-runtime")]
287impl<
288		System: topsoil_core::system::Config + IsInherent<Block::Extrinsic>,
289		Block: traits::Block<
290			Header = topsoil_core::system::pallet_prelude::HeaderFor<System>,
291			Hash = System::Hash,
292		>,
293		Context: Default,
294		UnsignedValidator,
295		AllPalletsWithSystem: OnRuntimeUpgrade
296			+ BeforeAllRuntimeMigrations
297			+ OnInitializeWithWeightRegistration<System>
298			+ OnIdle<BlockNumberFor<System>>
299			+ OnFinalize<BlockNumberFor<System>>
300			+ OffchainWorker<BlockNumberFor<System>>
301			+ OnPoll<BlockNumberFor<System>>
302			+ TryState<BlockNumberFor<System>>
303			+ TryDecodeEntireStorage,
304		COnRuntimeUpgrade: OnRuntimeUpgrade,
305	> Executive<System, Block, Context, UnsignedValidator, AllPalletsWithSystem, COnRuntimeUpgrade>
306where
307	Block::Extrinsic: Checkable<Context> + Codec,
308	CheckedOf<Block::Extrinsic, Context>: Applyable + GetDispatchInfo,
309	CallOf<Block::Extrinsic, Context>:
310		Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
311	OriginOf<Block::Extrinsic, Context>: From<Option<System::AccountId>>,
312	UnsignedValidator: ValidateUnsigned<Call = CallOf<Block::Extrinsic, Context>>,
313{
314	/// Execute given block, but don't as strict is the normal block execution.
315	///
316	/// Some checks can be disabled via:
317	///
318	/// - `state_root_check`
319	/// - `signature_check`
320	///
321	/// Should only be used for testing ONLY.
322	pub fn try_execute_block(
323		block: Block::LazyBlock,
324		state_root_check: bool,
325		signature_check: bool,
326		select: topsoil_try_runtime::TryStateSelect,
327	) -> Result<Weight, ExecutiveError> {
328		log::info!(
329			target: LOG_TARGET,
330			"try-runtime: executing block #{:?} / state root check: {:?} / signature check: {:?} / try-state-select: {:?}",
331			block.header().number(),
332			state_root_check,
333			signature_check,
334			select,
335		);
336
337		let mode = Self::initialize_block(block.header());
338		Self::initial_checks(block.header());
339
340		// Apply extrinsics:
341		let signature_check = if signature_check {
342			Block::Extrinsic::check
343		} else {
344			Block::Extrinsic::unchecked_into_checked_i_know_what_i_am_doing
345		};
346		Self::apply_extrinsics(mode, block.extrinsics(), |uxt, is_inherent| {
347			Self::do_apply_extrinsic(uxt, is_inherent, signature_check)
348		})?;
349
350		// In this case there were no transactions to trigger this state transition:
351		if !<topsoil_core::system::Pallet<System>>::inherents_applied() {
352			Self::inherents_applied();
353		}
354
355		// post-extrinsics book-keeping
356		<topsoil_core::system::Pallet<System>>::note_finished_extrinsics();
357		<System as topsoil_core::system::Config>::PostTransactions::post_transactions();
358
359		let header = block.header();
360		Self::on_idle_hook(*header.number());
361		Self::on_finalize_hook(*header.number());
362
363		// run the try-state checks of all pallets, ensuring they don't alter any state.
364		let _guard = topsoil_core::StorageNoopGuard::default();
365		<AllPalletsWithSystem as topsoil_core::traits::TryState<
366			BlockNumberFor<System>,
367		>>::try_state(*header.number(), select.clone())
368		.map_err(|e| {
369			log::error!(target: LOG_TARGET, "failure: {:?}", e);
370			ExecutiveError::Custom(e.into())
371		})?;
372		if select.any() {
373			let res = AllPalletsWithSystem::try_decode_entire_state();
374			Self::log_decode_result(res).map_err(|e| ExecutiveError::Custom(e.into()))?;
375		}
376		drop(_guard);
377
378		// do some of the checks that would normally happen in `final_checks`, but perhaps skip
379		// the state root check.
380		{
381			let new_header = <topsoil_core::system::Pallet<System>>::finalize();
382			let items_zip = header.digest().logs().iter().zip(new_header.digest().logs().iter());
383			for (header_item, computed_item) in items_zip {
384				header_item.check_equal(computed_item);
385				assert!(header_item == computed_item, "Digest item must match that calculated.");
386			}
387
388			if state_root_check {
389				let storage_root = new_header.state_root();
390				header.state_root().check_equal(storage_root);
391				assert!(
392					header.state_root() == storage_root,
393					"Storage root must match that calculated."
394				);
395			}
396
397			assert!(
398				header.extrinsics_root() == new_header.extrinsics_root(),
399				"Transaction trie root must be valid.",
400			);
401		}
402
403		log::info!(
404			target: LOG_TARGET,
405			"try-runtime: Block #{:?} successfully executed",
406			header.number(),
407		);
408
409		Ok(topsoil_core::system::Pallet::<System>::block_weight().total())
410	}
411
412	/// Execute all Migrations of this runtime.
413	///
414	/// The `checks` param determines whether to execute `pre/post_upgrade` and `try_state` hooks.
415	///
416	/// [`topsoil_core::system::LastRuntimeUpgrade`] is set to the current runtime version after
417	/// migrations execute. This is important for idempotency checks, because some migrations use
418	/// this value to determine whether or not they should execute.
419	///
420	/// This function runs `try_state` hooks for all pallets. Use
421	/// [`Self::try_runtime_upgrade_with_config`] if you need more control over which pallets'
422	/// `try_state` hooks to execute.
423	pub fn try_runtime_upgrade(checks: UpgradeCheckSelect) -> Result<Weight, TryRuntimeError> {
424		Self::try_runtime_upgrade_with_config(TryRuntimeUpgradeConfig::new(checks))
425	}
426
427	/// Execute all Migrations of this runtime with custom configuration.
428	///
429	/// This function provides more granular control over runtime upgrade testing compared to
430	/// [`Self::try_runtime_upgrade`]. Use [`TryRuntimeUpgradeConfig`] to specify which checks
431	/// to run and which pallets' try_state hooks to execute.
432	///
433	/// [`topsoil_core::system::LastRuntimeUpgrade`] is set to the current runtime version after
434	/// migrations execute. This is important for idempotency checks, because some migrations use
435	/// this value to determine whether or not they should execute.
436	pub fn try_runtime_upgrade_with_config(
437		config: TryRuntimeUpgradeConfig,
438	) -> Result<Weight, TryRuntimeError> {
439		let checks = config.checks;
440		let try_state_select = config.try_state_select;
441		let before_all_weight =
442			<AllPalletsWithSystem as BeforeAllRuntimeMigrations>::before_all_runtime_migrations();
443
444		let try_on_runtime_upgrade_weight =
445			<(
446				COnRuntimeUpgrade,
447				<System as topsoil_core::system::Config>::SingleBlockMigrations,
448				// We want to run the migrations before we call into the pallets as they may
449				// access any state that would then not be migrated.
450				AllPalletsWithSystem,
451			) as OnRuntimeUpgrade>::try_on_runtime_upgrade(checks.pre_and_post())?;
452
453		topsoil_core::system::LastRuntimeUpgrade::<System>::put(
454			topsoil_core::system::LastRuntimeUpgradeInfo::from(
455				<System::Version as topsoil_core::traits::Get<_>>::get(),
456			),
457		);
458
459		// Nothing should modify the state after the migrations ran:
460		let _guard = StorageNoopGuard::default();
461
462		// The state must be decodable:
463		if checks.any() {
464			let res = AllPalletsWithSystem::try_decode_entire_state();
465			Self::log_decode_result(res)?;
466		}
467
468		// Check all storage invariants:
469		if checks.try_state() {
470			AllPalletsWithSystem::try_state(
471				topsoil_core::system::Pallet::<System>::block_number(),
472				try_state_select,
473			)?;
474		}
475
476		Ok(before_all_weight.saturating_add(try_on_runtime_upgrade_weight))
477	}
478
479	/// Logs the result of trying to decode the entire state.
480	fn log_decode_result(
481		res: Result<usize, alloc::vec::Vec<TryDecodeEntireStorageError>>,
482	) -> Result<(), TryRuntimeError> {
483		match res {
484			Ok(bytes) => {
485				log::info!(
486					target: LOG_TARGET,
487					"✅ Entire runtime state decodes without error. {} bytes total.",
488					bytes
489				);
490
491				Ok(())
492			},
493			Err(errors) => {
494				log::error!(
495					target: LOG_TARGET,
496					"`try_decode_entire_state` failed with {} errors",
497					errors.len(),
498				);
499
500				for (i, err) in errors.iter().enumerate() {
501					// We log the short version to `error` and then the full debug info to `debug`:
502					log::error!(target: LOG_TARGET, "- {i}. error: {err}");
503					log::debug!(target: LOG_TARGET, "- {i}. error: {err:?}");
504				}
505
506				Err("`try_decode_entire_state` failed".into())
507			},
508		}
509	}
510}
511
512/// Extension trait for [`OnInitialize`].
513///
514/// It takes care to register the weight of each pallet directly after executing its
515/// `on_initialize`.
516///
517/// The trait is sealed.
518pub trait OnInitializeWithWeightRegistration<T: topsoil_core::system::Config> {
519	/// The actual logic that calls `on_initialize` and registers the weight.
520	fn on_initialize_with_weight_registration(_n: BlockNumberFor<T>) -> Weight;
521}
522
523topsoil_core::impl_for_tuples_attr! {
524	#[tuple_types_custom_trait_bound(OnInitialize<topsoil_core::system::pallet_prelude::BlockNumberFor<T>>)]
525	impl<T: topsoil_core::system::Config> OnInitializeWithWeightRegistration<T> for Tuple {
526		fn on_initialize_with_weight_registration(n: BlockNumberFor<T>) -> Weight {
527			let mut weight = Weight::zero();
528			for_tuples!( #(
529				let individual_weight = Tuple::on_initialize(n);
530
531				<topsoil_core::system::Pallet<T>>::register_extra_weight_unchecked(
532					individual_weight,
533					DispatchClass::Mandatory,
534				);
535
536				weight = weight.saturating_add(individual_weight);
537			)* );
538
539			weight
540		}
541	}
542}
543
544/// TODO: The `OnRuntimeUpgrade` generic parameter in `Executive` is deprecated and will be
545/// removed in a future version. Once removed, this `#[allow(deprecated)]` attribute
546/// can be safely deleted.
547#[allow(deprecated)]
548impl<
549		System: topsoil_core::system::Config + IsInherent<Block::Extrinsic>,
550		Block: traits::Block<
551			Header = topsoil_core::system::pallet_prelude::HeaderFor<System>,
552			Hash = System::Hash,
553		>,
554		Context: Default,
555		UnsignedValidator,
556		AllPalletsWithSystem: OnRuntimeUpgrade
557			+ BeforeAllRuntimeMigrations
558			+ OnInitializeWithWeightRegistration<System>
559			+ OnIdle<BlockNumberFor<System>>
560			+ OnFinalize<BlockNumberFor<System>>
561			+ OffchainWorker<BlockNumberFor<System>>
562			+ OnPoll<BlockNumberFor<System>>,
563		COnRuntimeUpgrade: OnRuntimeUpgrade,
564	> Executive<System, Block, Context, UnsignedValidator, AllPalletsWithSystem, COnRuntimeUpgrade>
565where
566	Block::Extrinsic: Checkable<Context> + Codec,
567	CheckedOf<Block::Extrinsic, Context>: Applyable + GetDispatchInfo,
568	CallOf<Block::Extrinsic, Context>:
569		Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
570	OriginOf<Block::Extrinsic, Context>: From<Option<System::AccountId>>,
571	UnsignedValidator: ValidateUnsigned<Call = CallOf<Block::Extrinsic, Context>>,
572{
573	/// Execute all `OnRuntimeUpgrade` of this runtime, and return the aggregate weight.
574	pub fn execute_on_runtime_upgrade() -> Weight {
575		let before_all_weight =
576			<AllPalletsWithSystem as BeforeAllRuntimeMigrations>::before_all_runtime_migrations();
577
578		let runtime_upgrade_weight = <(
579			COnRuntimeUpgrade,
580			<System as topsoil_core::system::Config>::SingleBlockMigrations,
581			// We want to run the migrations before we call into the pallets as they may
582			// access any state that would then not be migrated.
583			AllPalletsWithSystem,
584		) as OnRuntimeUpgrade>::on_runtime_upgrade();
585
586		before_all_weight.saturating_add(runtime_upgrade_weight)
587	}
588
589	/// Start the execution of a particular block.
590	pub fn initialize_block(
591		header: &topsoil_core::system::pallet_prelude::HeaderFor<System>,
592	) -> ExtrinsicInclusionMode {
593		subsoil::io::init_tracing();
594		subsoil::enter_span!(subsoil::tracing::Level::TRACE, "init_block");
595		let digests = Self::extract_pre_digest(header);
596		Self::initialize_block_impl(header.number(), header.parent_hash(), &digests);
597
598		Self::extrinsic_mode()
599	}
600
601	fn extrinsic_mode() -> ExtrinsicInclusionMode {
602		if <System as topsoil_core::system::Config>::MultiBlockMigrator::ongoing() {
603			ExtrinsicInclusionMode::OnlyInherents
604		} else {
605			ExtrinsicInclusionMode::AllExtrinsics
606		}
607	}
608
609	fn extract_pre_digest(header: &topsoil_core::system::pallet_prelude::HeaderFor<System>) -> Digest {
610		let mut digest = <Digest>::default();
611		header.digest().logs().iter().for_each(|d| {
612			if d.as_pre_runtime().is_some() {
613				digest.push(d.clone())
614			}
615		});
616		digest
617	}
618
619	fn initialize_block_impl(
620		block_number: &BlockNumberFor<System>,
621		parent_hash: &System::Hash,
622		digest: &Digest,
623	) {
624		// Reset events before apply runtime upgrade hook.
625		// This is required to preserve events from runtime upgrade hook.
626		// This means the format of all the event related storage must always be compatible.
627		<topsoil_core::system::Pallet<System>>::reset_events();
628
629		let mut weight = Weight::zero();
630		if Self::runtime_upgraded() {
631			weight = weight.saturating_add(Self::execute_on_runtime_upgrade());
632
633			topsoil_core::system::LastRuntimeUpgrade::<System>::put(
634				topsoil_core::system::LastRuntimeUpgradeInfo::from(
635					<System::Version as topsoil_core::traits::Get<_>>::get(),
636				),
637			);
638		}
639		<topsoil_core::system::Pallet<System>>::initialize(block_number, parent_hash, digest);
640
641		weight = System::BlockWeights::get().base_block.saturating_add(weight);
642		// Register the base block weight and optional `on_runtime_upgrade` weight.
643		<topsoil_core::system::Pallet<System>>::register_extra_weight_unchecked(
644			weight,
645			DispatchClass::Mandatory,
646		);
647
648		weight = weight
649			.saturating_add(<AllPalletsWithSystem as OnInitializeWithWeightRegistration<
650			System,
651		>>::on_initialize_with_weight_registration(*block_number));
652
653		log::debug!(
654			target: LOG_TARGET,
655			"[{block_number:?}]: Block initialization weight consumption: {weight:?}",
656		);
657
658		topsoil_core::system::Pallet::<System>::note_finished_initialize();
659		<System as topsoil_core::system::Config>::PreInherents::pre_inherents();
660	}
661
662	/// Returns if the runtime has been upgraded, based on [`topsoil_core::system::LastRuntimeUpgrade`].
663	fn runtime_upgraded() -> bool {
664		let last = topsoil_core::system::LastRuntimeUpgrade::<System>::get();
665		let current = <System::Version as topsoil_core::traits::Get<_>>::get();
666
667		last.map(|v| v.was_upgraded(&current)).unwrap_or(true)
668	}
669
670	fn initial_checks(header: &Block::Header) {
671		subsoil::enter_span!(subsoil::tracing::Level::TRACE, "initial_checks");
672
673		// Check that `parent_hash` is correct.
674		let n = *header.number();
675		assert!(
676			n > BlockNumberFor::<System>::zero()
677				&& <topsoil_core::system::Pallet<System>>::block_hash(
678					n - BlockNumberFor::<System>::one()
679				) == *header.parent_hash(),
680			"Parent hash should be valid.",
681		);
682	}
683
684	/// Actually execute all transitions for `block`.
685	pub fn execute_block(block: Block::LazyBlock) {
686		subsoil::io::init_tracing();
687		subsoil::within_span! {
688			subsoil::tracing::info_span!("execute_block", ?block);
689			// Execute `on_runtime_upgrade` and `on_initialize`.
690			let mode = Self::initialize_block(block.header());
691			Self::initial_checks(block.header());
692
693			let extrinsics = block.extrinsics();
694			if let Err(e) = Self::apply_extrinsics(
695				mode,
696				extrinsics,
697				|uxt, is_inherent| {
698					Self::do_apply_extrinsic(uxt, is_inherent, Block::Extrinsic::check)
699				}
700			) {
701				panic!("{:?}", e)
702			}
703
704			// In this case there were no transactions to trigger this state transition:
705			if !<topsoil_core::system::Pallet<System>>::inherents_applied() {
706				Self::inherents_applied();
707			}
708
709			<topsoil_core::system::Pallet<System>>::note_finished_extrinsics();
710			<System as topsoil_core::system::Config>::PostTransactions::post_transactions();
711
712			let header = block.header();
713			Self::on_idle_hook(*header.number());
714			Self::on_finalize_hook(*header.number());
715			Self::final_checks(&header);
716		}
717	}
718
719	/// Logic that runs directly after inherent application.
720	///
721	/// It advances the Multi-Block-Migrations or runs the `on_poll` hook.
722	pub fn inherents_applied() {
723		<topsoil_core::system::Pallet<System>>::note_inherents_applied();
724		<System as topsoil_core::system::Config>::PostInherents::post_inherents();
725
726		if <System as topsoil_core::system::Config>::MultiBlockMigrator::ongoing() {
727			let used_weight = <System as topsoil_core::system::Config>::MultiBlockMigrator::step();
728			<topsoil_core::system::Pallet<System>>::register_extra_weight_unchecked(
729				used_weight,
730				DispatchClass::Mandatory,
731			);
732		} else {
733			let block_number = <topsoil_core::system::Pallet<System>>::block_number();
734			Self::on_poll_hook(block_number);
735		}
736	}
737
738	/// Execute given extrinsics.
739	fn apply_extrinsics(
740		mode: ExtrinsicInclusionMode,
741		extrinsics: impl Iterator<Item = Result<Block::Extrinsic, codec::Error>>,
742		mut apply_extrinsic: impl FnMut(Block::Extrinsic, bool) -> ApplyExtrinsicResult,
743	) -> Result<(), ExecutiveError> {
744		let mut first_non_inherent_idx = 0;
745		for (idx, maybe_uxt) in extrinsics.into_iter().enumerate() {
746			let uxt = maybe_uxt.map_err(|_| ExecutiveError::UnableToDecodeExtrinsic)?;
747			let is_inherent = System::is_inherent(&uxt);
748			if is_inherent {
749				// Check if inherents are first
750				if first_non_inherent_idx != idx {
751					return Err(ExecutiveError::InvalidInherentPosition(idx));
752				}
753				first_non_inherent_idx += 1;
754			} else {
755				// Check if there are any forbidden non-inherents in the block.
756				if mode == ExtrinsicInclusionMode::OnlyInherents {
757					return Err(ExecutiveError::OnlyInherentsAllowed);
758				}
759			}
760
761			log::debug!(target: LOG_TARGET, "Executing transaction: {:?}", uxt);
762			if let Err(e) = apply_extrinsic(uxt, is_inherent) {
763				log::error!(
764					target: LOG_TARGET,
765					"Transaction({idx}) failed due to {e:?}. \
766					Aborting the rest of the block execution.",
767				);
768				return Err(ExecutiveError::ApplyExtrinsic(e.into()));
769			}
770		}
771
772		Ok(())
773	}
774
775	/// Finalize the block - it is up the caller to ensure that all header fields are valid
776	/// except state-root.
777	// Note: Only used by the block builder - not Executive itself.
778	pub fn finalize_block() -> topsoil_core::system::pallet_prelude::HeaderFor<System> {
779		subsoil::io::init_tracing();
780		subsoil::enter_span!(subsoil::tracing::Level::TRACE, "finalize_block");
781
782		// In this case there were no transactions to trigger this state transition:
783		if !<topsoil_core::system::Pallet<System>>::inherents_applied() {
784			Self::inherents_applied();
785		}
786
787		<topsoil_core::system::Pallet<System>>::note_finished_extrinsics();
788		<System as topsoil_core::system::Config>::PostTransactions::post_transactions();
789		let block_number = <topsoil_core::system::Pallet<System>>::block_number();
790		Self::on_idle_hook(block_number);
791		Self::on_finalize_hook(block_number);
792		<topsoil_core::system::Pallet<System>>::finalize()
793	}
794
795	/// Run the `on_idle` hook of all pallet, but only if there is weight remaining and there are no
796	/// ongoing MBMs.
797	fn on_idle_hook(block_number: NumberFor<Block>) {
798		if <System as topsoil_core::system::Config>::MultiBlockMigrator::ongoing() {
799			return;
800		}
801
802		let weight = <topsoil_core::system::Pallet<System>>::block_weight();
803		let max_weight = <System::BlockWeights as topsoil_core::traits::Get<_>>::get().max_block;
804		let remaining_weight = max_weight.saturating_sub(weight.total());
805
806		if remaining_weight.all_gt(Weight::zero()) {
807			let used_weight = <AllPalletsWithSystem as OnIdle<BlockNumberFor<System>>>::on_idle(
808				block_number,
809				remaining_weight,
810			);
811			<topsoil_core::system::Pallet<System>>::register_extra_weight_unchecked(
812				used_weight,
813				DispatchClass::Mandatory,
814			);
815		}
816	}
817
818	fn on_poll_hook(block_number: NumberFor<Block>) {
819		defensive_assert!(
820			!<System as topsoil_core::system::Config>::MultiBlockMigrator::ongoing(),
821			"on_poll should not be called during migrations"
822		);
823
824		let weight = <topsoil_core::system::Pallet<System>>::block_weight();
825		let max_weight = <System::BlockWeights as topsoil_core::traits::Get<_>>::get().max_block;
826		let remaining = max_weight.saturating_sub(weight.total());
827
828		if remaining.all_gt(Weight::zero()) {
829			let mut meter = WeightMeter::with_limit(remaining);
830			<AllPalletsWithSystem as OnPoll<BlockNumberFor<System>>>::on_poll(
831				block_number,
832				&mut meter,
833			);
834			<topsoil_core::system::Pallet<System>>::register_extra_weight_unchecked(
835				meter.consumed(),
836				DispatchClass::Mandatory,
837			);
838		}
839	}
840
841	/// Run the `on_finalize` hook of all pallet.
842	fn on_finalize_hook(block_number: NumberFor<Block>) {
843		<AllPalletsWithSystem as OnFinalize<BlockNumberFor<System>>>::on_finalize(block_number);
844	}
845
846	/// Apply extrinsic outside of the block execution function.
847	///
848	/// This doesn't attempt to validate anything regarding the block, but it builds a list of uxt
849	/// hashes.
850	fn do_apply_extrinsic(
851		uxt: Block::Extrinsic,
852		is_inherent: bool,
853		check: impl FnOnce(
854			Block::Extrinsic,
855			&Context,
856		) -> Result<CheckedOf<Block::Extrinsic, Context>, TransactionValidityError>,
857	) -> ApplyExtrinsicResult {
858		subsoil::io::init_tracing();
859		let encoded = uxt.encode();
860		let encoded_len = encoded.len();
861		subsoil::enter_span!(subsoil::tracing::info_span!("apply_extrinsic",
862			ext=?subsoil::core::hexdisplay::HexDisplay::from(&encoded)));
863
864		let uxt = <Block::Extrinsic as codec::DecodeLimit>::decode_all_with_depth_limit(
865			MAX_EXTRINSIC_DEPTH,
866			&mut &encoded[..],
867		)
868		.map_err(|_| InvalidTransaction::Call)?;
869
870		// Verify that the signature is good.
871		let xt = check(uxt, &Context::default())?;
872
873		let dispatch_info = xt.get_dispatch_info();
874
875		if !is_inherent && !<topsoil_core::system::Pallet<System>>::inherents_applied() {
876			Self::inherents_applied();
877		}
878
879		// We don't need to make sure to `note_extrinsic` only after we know it's going to be
880		// executed to prevent it from leaking in storage since at this point, it will either
881		// execute or panic (and revert storage changes).
882		<topsoil_core::system::Pallet<System>>::note_extrinsic(encoded);
883
884		// AUDIT: Under no circumstances may this function panic from here onwards.
885
886		let r = Applyable::apply::<UnsignedValidator>(xt, &dispatch_info, encoded_len)?;
887
888		// Mandatory(inherents) are not allowed to fail.
889		//
890		// The entire block should be discarded if an inherent fails to apply. Otherwise
891		// it may open an attack vector.
892		if r.is_err() && dispatch_info.class == DispatchClass::Mandatory {
893			return Err(InvalidTransaction::BadMandatory.into());
894		}
895
896		<topsoil_core::system::Pallet<System>>::note_applied_extrinsic(&r, dispatch_info);
897
898		Ok(r.map(|_| ()).map_err(|e| e.error))
899	}
900
901	/// Apply extrinsic outside of the block execution function.
902	///
903	/// This doesn't attempt to validate anything regarding the block, but it builds a list of uxt
904	/// hashes.
905	pub fn apply_extrinsic(uxt: Block::Extrinsic) -> ApplyExtrinsicResult {
906		let is_inherent = System::is_inherent(&uxt);
907		Self::do_apply_extrinsic(uxt, is_inherent, Block::Extrinsic::check)
908	}
909
910	fn final_checks(header: &topsoil_core::system::pallet_prelude::HeaderFor<System>) {
911		subsoil::enter_span!(subsoil::tracing::Level::TRACE, "final_checks");
912		// remove temporaries
913		let new_header = <topsoil_core::system::Pallet<System>>::finalize();
914
915		// check digest
916		assert_eq!(
917			header.digest().logs().len(),
918			new_header.digest().logs().len(),
919			"Number of digest items must match that calculated."
920		);
921		let items_zip = header.digest().logs().iter().zip(new_header.digest().logs().iter());
922		for (header_item, computed_item) in items_zip {
923			header_item.check_equal(computed_item);
924			assert!(header_item == computed_item, "Digest item must match that calculated.");
925		}
926
927		// check storage root.
928		let storage_root = new_header.state_root();
929		header.state_root().check_equal(storage_root);
930		assert!(header.state_root() == storage_root, "Storage root must match that calculated.");
931
932		assert!(
933			header.extrinsics_root() == new_header.extrinsics_root(),
934			"Transaction trie root must be valid.",
935		);
936	}
937
938	/// Check a given signed transaction for validity. This doesn't execute any
939	/// side-effects; it merely checks whether the transaction would panic if it were included or
940	/// not.
941	///
942	/// Changes made to storage should be discarded.
943	pub fn validate_transaction(
944		source: TransactionSource,
945		uxt: Block::Extrinsic,
946		block_hash: Block::Hash,
947	) -> TransactionValidity {
948		subsoil::io::init_tracing();
949		use subsoil::{enter_span, within_span};
950
951		<topsoil_core::system::Pallet<System>>::initialize(
952			&(topsoil_core::system::Pallet::<System>::block_number() + One::one()),
953			&block_hash,
954			&Default::default(),
955		);
956
957		enter_span! { subsoil::tracing::Level::TRACE, "validate_transaction" };
958
959		let encoded = within_span! { subsoil::tracing::Level::TRACE, "using_encoded";
960			uxt.encode()
961		};
962
963		let uxt = <Block::Extrinsic as codec::DecodeLimit>::decode_all_with_depth_limit(
964			MAX_EXTRINSIC_DEPTH,
965			&mut &encoded[..],
966		)
967		.map_err(|_| InvalidTransaction::Call)?;
968
969		let xt = within_span! { subsoil::tracing::Level::TRACE, "check";
970			uxt.check(&Default::default())
971		}?;
972
973		let dispatch_info = within_span! { subsoil::tracing::Level::TRACE, "dispatch_info";
974			xt.get_dispatch_info()
975		};
976
977		if dispatch_info.class == DispatchClass::Mandatory {
978			return Err(InvalidTransaction::MandatoryValidation.into());
979		}
980
981		within_span! {
982			subsoil::tracing::Level::TRACE, "validate";
983			xt.validate::<UnsignedValidator>(source, &dispatch_info, encoded.len())
984		}
985	}
986
987	/// Start an offchain worker and generate extrinsics.
988	pub fn offchain_worker(header: &topsoil_core::system::pallet_prelude::HeaderFor<System>) {
989		subsoil::io::init_tracing();
990		// We need to keep events available for offchain workers,
991		// hence we initialize the block manually.
992		// OffchainWorker RuntimeApi should skip initialization.
993		let digests = header.digest().clone();
994
995		// Let's deposit all the logs we are not yet aware of. These are the logs set by the `node`.
996		let existing_digest = topsoil_core::system::Pallet::<System>::digest();
997		for digest in digests.logs().iter().filter(|d| !existing_digest.logs.contains(d)) {
998			topsoil_core::system::Pallet::<System>::deposit_log(digest.clone());
999		}
1000
1001		// Initialize the intra block entropy, which is maybe used by offchain workers.
1002		topsoil_core::system::Pallet::<System>::initialize_intra_block_entropy(header.parent_hash());
1003
1004		// Frame system only inserts the parent hash into the block hashes as normally we don't know
1005		// the hash for the header before. However, here we are aware of the hash and we can add it
1006		// as well.
1007		topsoil_core::system::BlockHash::<System>::insert(header.number(), header.hash());
1008
1009		<AllPalletsWithSystem as OffchainWorker<BlockNumberFor<System>>>::offchain_worker(
1010			*header.number(),
1011		)
1012	}
1013}