Skip to main content

domain_pallet_executive/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Domain Executive Module
19//!
20//! This module is derived from frame_executive with some custom modifications for
21//! including the failed extrinsic during `pre/post_dispatch` in the block and
22//! collecting the intermediate storage roots in the block execution required for
23//! the fraud proof of decoupled execution in Subspace.
24
25#![cfg_attr(not(feature = "std"), no_std)]
26// TODO: Remove once FRAME macros stop generating `.clone()` calls on `Copy` types with user spans
27#![expect(clippy::clone_on_copy, reason = "Comes from Substrate")]
28
29#[cfg(feature = "runtime-benchmarks")]
30mod benchmarking;
31#[cfg(test)]
32mod mock;
33#[cfg(test)]
34mod tests;
35pub mod weights;
36
37#[cfg(not(feature = "std"))]
38extern crate alloc;
39
40use frame_executive::OnInitializeWithWeightRegistration;
41use frame_support::dispatch::{
42    DispatchClass, DispatchErrorWithPostInfo, DispatchInfo, GetDispatchInfo, Pays, PostDispatchInfo,
43};
44use frame_support::storage::with_storage_layer;
45use frame_support::traits::fungible::{Inspect, Mutate};
46use frame_support::traits::tokens::{Fortitude, Precision, Preservation};
47use frame_support::traits::{
48    BeforeAllRuntimeMigrations, ExecuteBlock, Get, IsInherent, OffchainWorker, OnFinalize, OnIdle,
49    OnPoll, OnRuntimeUpgrade, PostTransactions,
50};
51use frame_support::weights::{Weight, WeightToFee};
52use frame_system::pallet_prelude::*;
53pub use pallet::*;
54use parity_scale_codec::{Codec, Encode};
55use sp_runtime::traits::{
56    Applyable, Block as BlockT, CheckEqual, Checkable, Dispatchable, Header, LazyBlock, NumberFor,
57    One, ValidateUnsigned, Zero,
58};
59use sp_runtime::transaction_validity::{
60    InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
61};
62use sp_runtime::{ApplyExtrinsicResult, DispatchError, ExtrinsicInclusionMode};
63use sp_std::marker::PhantomData;
64use sp_std::prelude::*;
65pub use weights::WeightInfo;
66
67pub type CheckedOf<E, C> = <E as Checkable<C>>::Checked;
68pub type CallOf<E, C> = <CheckedOf<E, C> as Applyable>::Call;
69pub type OriginOf<E, C> = <CallOf<E, C> as Dispatchable>::RuntimeOrigin;
70
71/// Trait trait used to charge the extrinsic storage.
72pub trait ExtrinsicStorageFees<T: Config> {
73    /// Extracts signer from given extrinsic and its dispatch info.
74    fn extract_signer(xt: ExtrinsicOf<T>) -> (Option<AccountIdOf<T>>, DispatchInfo);
75    /// Hook to note operator rewards for charged storage fees.
76    fn on_storage_fees_charged(
77        charged_fees: BalanceOf<T>,
78        tx_size: u32,
79    ) -> Result<(), TransactionValidityError>;
80}
81
82type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
83type BalanceOf<T> = <<T as Config>::Currency as Inspect<AccountIdOf<T>>>::Balance;
84type ExtrinsicOf<T> = <BlockOf<T> as BlockT>::Extrinsic;
85type BlockOf<T> = <T as frame_system::Config>::Block;
86type BlockHashOf<T> = <BlockOf<T> as BlockT>::Hash;
87
88#[frame_support::pallet]
89mod pallet {
90    use crate::weights::WeightInfo;
91    use crate::{BalanceOf, ExtrinsicStorageFees};
92    #[cfg(not(feature = "std"))]
93    use alloc::vec::Vec;
94    use frame_support::pallet_prelude::*;
95    use frame_support::traits::fungible::Mutate;
96    use frame_support::weights::WeightToFee;
97    use frame_system::SetCode;
98    use frame_system::pallet_prelude::*;
99    use sp_executive::{INHERENT_IDENTIFIER, InherentError, InherentType};
100
101    #[pallet::config]
102    pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {
103        type WeightInfo: WeightInfo;
104        type Currency: Mutate<Self::AccountId>;
105        type LengthToFee: WeightToFee<Balance = BalanceOf<Self>>;
106        type ExtrinsicStorageFees: ExtrinsicStorageFees<Self>;
107    }
108
109    #[pallet::pallet]
110    #[pallet::without_storage_info]
111    pub struct Pallet<T>(_);
112
113    #[pallet::call]
114    impl<T: Config> Pallet<T> {
115        /// Sets new runtime code after doing necessary checks.
116        /// Same as frame_system::Call::set_code but without root origin.
117        #[pallet::call_index(0)]
118        #[pallet::weight((T::WeightInfo::set_code(), DispatchClass::Mandatory))]
119        pub fn set_code(origin: OriginFor<T>, code: Vec<u8>) -> DispatchResult {
120            ensure_none(origin)?;
121            // we dont need to check version here since the version check already happens
122            // on the consensus side before issuing a domain runtime upgrade.
123            <frame_system::pallet::Pallet<T>>::can_set_code(&code, false).into_result()?;
124            <T as frame_system::Config>::OnSetCode::set_code(code)?;
125            Ok(())
126        }
127    }
128
129    #[pallet::inherent]
130    impl<T: Config> ProvideInherent for Pallet<T> {
131        type Call = Call<T>;
132        type Error = InherentError;
133        const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
134
135        fn create_inherent(data: &InherentData) -> Option<Self::Call> {
136            let inherent_data = data
137                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
138                .expect("Executive inherent data not correctly encoded")
139                .expect("Executive inherent data must be provided");
140
141            inherent_data.maybe_code.map(|code| Call::set_code { code })
142        }
143
144        fn is_inherent_required(data: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
145            let inherent_data = data
146                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
147                .expect("Executive inherent data not correctly encoded")
148                .expect("Executive inherent data must be provided");
149
150            Ok(if inherent_data.maybe_code.is_none() {
151                None
152            } else {
153                Some(InherentError::MissingRuntimeCode)
154            })
155        }
156
157        fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
158            let inherent_data = data
159                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
160                .expect("Executive inherent data not correctly encoded")
161                .expect("Executive inherent data must be provided");
162
163            if let Some(provided_code) = inherent_data.maybe_code {
164                if let Call::set_code { code } = call
165                    && code != &provided_code
166                {
167                    return Err(InherentError::IncorrectRuntimeCode);
168                }
169            } else {
170                return Err(InherentError::MissingRuntimeCode);
171            }
172
173            Ok(())
174        }
175
176        fn is_inherent(call: &Self::Call) -> bool {
177            matches!(call, Call::set_code { .. })
178        }
179    }
180
181    #[pallet::event]
182    pub enum Event<T: Config> {}
183
184    #[pallet::hooks]
185    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
186        fn on_initialize(_block_number: BlockNumberFor<T>) -> Weight {
187            Weight::zero()
188        }
189    }
190}
191
192/// Same semantics with `frame_executive::Executive`.
193///
194/// One extra generic parameter:
195/// - `ExecutiveConfig`: Something that implements `domain_pallet_executive::Config`.
196pub struct Executive<
197    ExecutiveConfig,
198    Context,
199    UnsignedValidator,
200    AllPalletsWithSystem,
201    OnRuntimeUpgrade = (),
202>(
203    PhantomData<(
204        ExecutiveConfig,
205        Context,
206        UnsignedValidator,
207        AllPalletsWithSystem,
208        OnRuntimeUpgrade,
209    )>,
210);
211
212impl<
213    ExecutiveConfig: Config + frame_system::Config + IsInherent<ExtrinsicOf<ExecutiveConfig>>,
214    Context: Default,
215    UnsignedValidator,
216    AllPalletsWithSystem: OnRuntimeUpgrade
217        + BeforeAllRuntimeMigrations
218        + OnInitializeWithWeightRegistration<ExecutiveConfig>
219        + OnIdle<BlockNumberFor<ExecutiveConfig>>
220        + OnFinalize<BlockNumberFor<ExecutiveConfig>>
221        + OffchainWorker<BlockNumberFor<ExecutiveConfig>>
222        + OnPoll<BlockNumberFor<ExecutiveConfig>>,
223    COnRuntimeUpgrade: OnRuntimeUpgrade,
224> ExecuteBlock<BlockOf<ExecutiveConfig>>
225    for Executive<
226        ExecutiveConfig,
227        Context,
228        UnsignedValidator,
229        AllPalletsWithSystem,
230        COnRuntimeUpgrade,
231    >
232where
233    ExtrinsicOf<ExecutiveConfig>: Checkable<Context> + Codec,
234    CheckedOf<ExtrinsicOf<ExecutiveConfig>, Context>: Applyable + GetDispatchInfo,
235    CallOf<ExtrinsicOf<ExecutiveConfig>, Context>:
236        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
237    OriginOf<ExtrinsicOf<ExecutiveConfig>, Context>: From<Option<AccountIdOf<ExecutiveConfig>>>,
238    UnsignedValidator: ValidateUnsigned<Call = CallOf<ExtrinsicOf<ExecutiveConfig>, Context>>,
239{
240    fn execute_block(block: <BlockOf<ExecutiveConfig> as BlockT>::LazyBlock) {
241        Executive::<
242            ExecutiveConfig,
243            Context,
244            UnsignedValidator,
245            AllPalletsWithSystem,
246            COnRuntimeUpgrade,
247        >::execute_block(block);
248    }
249}
250
251impl<
252    ExecutiveConfig: Config + frame_system::Config + IsInherent<ExtrinsicOf<ExecutiveConfig>>,
253    Context: Default,
254    UnsignedValidator,
255    AllPalletsWithSystem: OnRuntimeUpgrade
256        + BeforeAllRuntimeMigrations
257        + OnInitializeWithWeightRegistration<ExecutiveConfig>
258        + OnIdle<BlockNumberFor<ExecutiveConfig>>
259        + OnFinalize<BlockNumberFor<ExecutiveConfig>>
260        + OffchainWorker<BlockNumberFor<ExecutiveConfig>>
261        + OnPoll<BlockNumberFor<ExecutiveConfig>>,
262    COnRuntimeUpgrade: OnRuntimeUpgrade,
263> Executive<ExecutiveConfig, Context, UnsignedValidator, AllPalletsWithSystem, COnRuntimeUpgrade>
264where
265    ExtrinsicOf<ExecutiveConfig>: Checkable<Context> + Codec,
266    CheckedOf<ExtrinsicOf<ExecutiveConfig>, Context>: Applyable + GetDispatchInfo,
267    CallOf<ExtrinsicOf<ExecutiveConfig>, Context>:
268        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
269    OriginOf<ExtrinsicOf<ExecutiveConfig>, Context>: From<Option<AccountIdOf<ExecutiveConfig>>>,
270    UnsignedValidator: ValidateUnsigned<Call = CallOf<ExtrinsicOf<ExecutiveConfig>, Context>>,
271{
272    /// Returns the latest storage root.
273    pub fn storage_root() -> Vec<u8> {
274        let version = <ExecutiveConfig as frame_system::Config>::Version::get().state_version();
275        sp_io::storage::root(version)
276    }
277
278    /// Wrapped `frame_executive::Executive::execute_on_runtime_upgrade`.
279    pub fn execute_on_runtime_upgrade() -> Weight {
280        frame_executive::Executive::<
281            ExecutiveConfig,
282            BlockOf<ExecutiveConfig>,
283            Context,
284            UnsignedValidator,
285            AllPalletsWithSystem,
286            COnRuntimeUpgrade,
287        >::execute_on_runtime_upgrade()
288    }
289
290    /// Wrapped `frame_executive::Executive::initialize_block`.
291    ///
292    /// Note the storage root in the end.
293    pub fn initialize_block(header: &HeaderFor<ExecutiveConfig>) -> ExtrinsicInclusionMode {
294        frame_executive::Executive::<
295            ExecutiveConfig,
296            BlockOf<ExecutiveConfig>,
297            Context,
298            UnsignedValidator,
299            AllPalletsWithSystem,
300            COnRuntimeUpgrade,
301        >::initialize_block(header)
302    }
303
304    /// Matches upstream `frame_executive::Executive::initial_checks`.
305    /// Only checks parent hash — inherent ordering is checked inline in `apply_extrinsics`.
306    fn initial_checks(header: &HeaderFor<ExecutiveConfig>) {
307        sp_tracing::enter_span!(sp_tracing::Level::TRACE, "initial_checks");
308
309        // Check that `parent_hash` is correct.
310        let n = *header.number();
311        assert!(
312            n > BlockNumberFor::<ExecutiveConfig>::zero()
313                && <frame_system::Pallet<ExecutiveConfig>>::block_hash(
314                    n - BlockNumberFor::<ExecutiveConfig>::one()
315                ) == *header.parent_hash(),
316            "Parent hash should be valid.",
317        );
318    }
319
320    /// Wrapped `frame_executive::Executive::execute_block`.
321    ///
322    /// The purpose is to use our custom [`Executive::apply_extrinsic`] and
323    /// the [`Executive::finalize_block`] logic.
324    ///
325    /// Matches upstream structure: `initial_checks` only validates parent hash,
326    /// inherent ordering is checked inline in `apply_extrinsics`.
327    pub fn execute_block(block: <BlockOf<ExecutiveConfig> as BlockT>::LazyBlock) {
328        sp_io::init_tracing();
329        sp_tracing::within_span! {
330            sp_tracing::info_span!("execute_block", ?block);
331            // Execute `on_runtime_upgrade` and `on_initialize`.
332            let mode = Self::initialize_block(block.header());
333            Self::initial_checks(block.header());
334
335            let extrinsics = block.extrinsics();
336            Self::apply_extrinsics(mode, extrinsics);
337
338            // In this case there were no transactions to trigger this state transition:
339            if !<frame_system::Pallet<ExecutiveConfig>>::inherents_applied() {
340                frame_executive::Executive::<
341                    ExecutiveConfig,
342                    BlockOf<ExecutiveConfig>,
343                    Context,
344                    UnsignedValidator,
345                    AllPalletsWithSystem,
346                    COnRuntimeUpgrade,
347                >::inherents_applied();
348            }
349
350            <frame_system::Pallet<ExecutiveConfig>>::note_finished_extrinsics();
351            <ExecutiveConfig as frame_system::Config>::PostTransactions::post_transactions();
352
353            let header = block.header();
354            Self::on_idle_hook(*header.number());
355            Self::on_finalize_hook(*header.number());
356            Self::final_checks(header);
357        }
358    }
359
360    /// Matches upstream `frame_executive::Executive::apply_extrinsics`.
361    /// Checks inherent ordering inline per-extrinsic.
362    fn apply_extrinsics(
363        mode: ExtrinsicInclusionMode,
364        extrinsics: impl Iterator<
365            Item = Result<ExtrinsicOf<ExecutiveConfig>, parity_scale_codec::Error>,
366        >,
367    ) {
368        let mut first_non_inherent_idx = 0;
369        for (idx, maybe_uxt) in extrinsics.into_iter().enumerate() {
370            let uxt = maybe_uxt
371                .unwrap_or_else(|err| panic!("Failed to decode extrinsic at index {idx}: {err}"));
372            let is_inherent = ExecutiveConfig::is_inherent(&uxt);
373            if is_inherent {
374                if first_non_inherent_idx != idx {
375                    panic!("Invalid inherent position for extrinsic at index {idx}");
376                }
377                first_non_inherent_idx += 1;
378            } else if mode == ExtrinsicInclusionMode::OnlyInherents {
379                panic!("Only inherents are allowed in this block")
380            }
381
382            if let Err(e) = Self::apply_extrinsic(uxt) {
383                let err: &'static str = e.into();
384                panic!("{}", err)
385            }
386        }
387    }
388
389    /// Wrapped `frame_executive::Executive::finalize_block`.
390    pub fn finalize_block() -> HeaderFor<ExecutiveConfig> {
391        frame_executive::Executive::<
392            ExecutiveConfig,
393            BlockOf<ExecutiveConfig>,
394            Context,
395            UnsignedValidator,
396            AllPalletsWithSystem,
397            COnRuntimeUpgrade,
398        >::finalize_block()
399    }
400
401    /// Matches upstream `frame_executive::Executive::on_idle_hook`.
402    fn on_idle_hook(block_number: NumberFor<BlockOf<ExecutiveConfig>>) {
403        let weight = <frame_system::Pallet<ExecutiveConfig>>::block_weight();
404        let max_weight = <<ExecutiveConfig as frame_system::Config>::BlockWeights as frame_support::traits::Get<_>>::get().max_block;
405        let remaining_weight = max_weight.saturating_sub(weight.total());
406
407        if remaining_weight.all_gt(Weight::zero()) {
408            let used_weight = AllPalletsWithSystem::on_idle(block_number, remaining_weight);
409            <frame_system::Pallet<ExecutiveConfig>>::register_extra_weight_unchecked(
410                used_weight,
411                DispatchClass::Mandatory,
412            );
413        }
414    }
415
416    /// Matches upstream `frame_executive::Executive::on_finalize_hook`.
417    fn on_finalize_hook(block_number: NumberFor<BlockOf<ExecutiveConfig>>) {
418        AllPalletsWithSystem::on_finalize(block_number);
419    }
420
421    /// Wrapped `frame_executive::Executive::apply_extrinsic`.
422    ///
423    /// Note the storage root in the beginning.
424    pub fn apply_extrinsic(uxt: ExtrinsicOf<ExecutiveConfig>) -> ApplyExtrinsicResult {
425        // apply the extrinsic within another transaction so that changes can be reverted.
426        let res = with_storage_layer(|| {
427            frame_executive::Executive::<
428                ExecutiveConfig,
429                BlockOf<ExecutiveConfig>,
430                Context,
431                UnsignedValidator,
432                AllPalletsWithSystem,
433                COnRuntimeUpgrade,
434            >::apply_extrinsic(uxt.clone())
435            .map_err(|err| DispatchError::Other(err.into()))
436        });
437
438        // apply extrinsic failed with transaction validity error
439        // this could happen for following scenarios
440        // - Bad extrinsic Signature
441        //      This extrinsic will be ignored by the operators during the bundle check
442        //      and marks such bundle as Invalid.
443        // - Extrinsic execution failed
444        //      There are multiple scenarios why this can happen
445        //      - Inherent extrinsic failed. If this happens, we should fail to apply extrinsic
446        //      - Pre and Post dispatch fails. Check the test `test_domain_block_builder_include_ext_with_failed_predispatch`
447        //        why this could happen. If it fail due to this, then we revert the inner storage changes
448        //        but still include extrinsic so that we can clear inconsistency between block body and trace roots.
449        match res {
450            Ok(dispatch_outcome) => Ok(dispatch_outcome),
451            Err(err) => {
452                let encoded = uxt.encode();
453                let (maybe_signer, dispatch_info) =
454                    ExecutiveConfig::ExtrinsicStorageFees::extract_signer(uxt);
455                // if this is mandatory extrinsic, then transaction should not execute
456                // we should fail here.
457                if dispatch_info.class == DispatchClass::Mandatory {
458                    return Err(TransactionValidityError::Invalid(
459                        InvalidTransaction::MandatoryValidation,
460                    ));
461                }
462
463                // charge signer for extrinsic storage
464                if let Some(signer) = maybe_signer {
465                    let storage_fees = ExecutiveConfig::LengthToFee::weight_to_fee(
466                        &Weight::from_parts(encoded.len() as u64, 0),
467                    );
468
469                    // best effort to charge the fees to signer.
470                    // if signer does not have enough balance, we continue
471                    let maybe_charged_fees = ExecutiveConfig::Currency::burn_from(
472                        &signer,
473                        storage_fees,
474                        Preservation::Expendable,
475                        Precision::BestEffort,
476                        Fortitude::Force,
477                    );
478
479                    if let Ok(charged_fees) = maybe_charged_fees {
480                        ExecutiveConfig::ExtrinsicStorageFees::on_storage_fees_charged(
481                            charged_fees,
482                            encoded.len() as u32,
483                        )?;
484                    }
485                }
486
487                // note the extrinsic into system storage
488                <frame_system::Pallet<ExecutiveConfig>>::note_extrinsic(encoded);
489
490                // note extrinsic applied. it pays no fees since there was no execution.
491                // we also set the weight to zero since there was no execution done.
492                let r = Err(DispatchErrorWithPostInfo {
493                    post_info: PostDispatchInfo {
494                        actual_weight: None,
495                        pays_fee: Pays::No,
496                    },
497                    error: err,
498                });
499
500                <frame_system::Pallet<ExecutiveConfig>>::note_applied_extrinsic(&r, dispatch_info);
501                Ok(Err(err))
502            }
503        }
504    }
505
506    // TODO: https://github.com/paritytech/substrate/issues/10711
507    fn final_checks(header: &HeaderFor<ExecutiveConfig>) {
508        sp_tracing::enter_span!(sp_tracing::Level::TRACE, "final_checks");
509        // remove temporaries
510        let new_header = <frame_system::Pallet<ExecutiveConfig>>::finalize();
511
512        // check digest
513        assert_eq!(
514            header.digest().logs().len(),
515            new_header.digest().logs().len(),
516            "Number of digest items must match that calculated."
517        );
518        let items_zip = header
519            .digest()
520            .logs()
521            .iter()
522            .zip(new_header.digest().logs().iter());
523        for (header_item, computed_item) in items_zip {
524            header_item.check_equal(computed_item);
525            assert_eq!(
526                header_item, computed_item,
527                "Digest item must match that calculated."
528            );
529        }
530
531        // check storage root.
532        let storage_root = new_header.state_root();
533        header.state_root().check_equal(storage_root);
534        assert!(
535            header.state_root() == storage_root,
536            "Storage root must match that calculated."
537        );
538
539        assert!(
540            header.extrinsics_root() == new_header.extrinsics_root(),
541            "Transaction trie root must be valid.",
542        );
543    }
544
545    /// Wrapped `frame_executive::Executive::validate_transaction`.
546    pub fn validate_transaction(
547        source: TransactionSource,
548        uxt: ExtrinsicOf<ExecutiveConfig>,
549        block_hash: BlockHashOf<ExecutiveConfig>,
550    ) -> TransactionValidity {
551        frame_executive::Executive::<
552            ExecutiveConfig,
553            BlockOf<ExecutiveConfig>,
554            Context,
555            UnsignedValidator,
556            AllPalletsWithSystem,
557            COnRuntimeUpgrade,
558        >::validate_transaction(source, uxt, block_hash)
559    }
560
561    /// Wrapped `frame_executive::Executive::offchain_worker`.
562    pub fn offchain_worker(header: &HeaderFor<ExecutiveConfig>) {
563        frame_executive::Executive::<
564            ExecutiveConfig,
565            BlockOf<ExecutiveConfig>,
566            Context,
567            UnsignedValidator,
568            AllPalletsWithSystem,
569            COnRuntimeUpgrade,
570        >::offchain_worker(header)
571    }
572}