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