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_support::defensive_assert;
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, EnsureInherentsAreFirst, ExecuteBlock, Get, OffchainWorker,
47    OnFinalize, OnIdle, OnInitialize, 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, NumberFor, One,
55    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::*;
63
64pub type CheckedOf<E, C> = <E as Checkable<C>>::Checked;
65pub type CallOf<E, C> = <CheckedOf<E, C> as Applyable>::Call;
66pub type OriginOf<E, C> = <CallOf<E, C> as Dispatchable>::RuntimeOrigin;
67
68/// Trait trait used to charge the extrinsic storage.
69pub trait ExtrinsicStorageFees<T: Config> {
70    /// Extracts signer from given extrinsic and its dispatch info.
71    fn extract_signer(xt: ExtrinsicOf<T>) -> (Option<AccountIdOf<T>>, DispatchInfo);
72    /// Hook to note operator rewards for charged storage fees.
73    fn on_storage_fees_charged(
74        charged_fees: BalanceOf<T>,
75        tx_size: u32,
76    ) -> Result<(), TransactionValidityError>;
77}
78
79type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
80type BalanceOf<T> = <<T as Config>::Currency as Inspect<AccountIdOf<T>>>::Balance;
81type ExtrinsicOf<T> = <BlockOf<T> as BlockT>::Extrinsic;
82type BlockOf<T> = <T as frame_system::Config>::Block;
83type BlockHashOf<T> = <BlockOf<T> as BlockT>::Hash;
84
85// TODO: not store the intermediate storage root in the state but
86// calculate the storage root outside the runtime after executing the extrinsic directly.
87#[frame_support::pallet]
88mod pallet {
89    use crate::weights::WeightInfo;
90    use crate::{BalanceOf, ExtrinsicStorageFees};
91    #[cfg(not(feature = "std"))]
92    use alloc::vec::Vec;
93    use frame_support::pallet_prelude::*;
94    use frame_support::traits::fungible::Mutate;
95    use frame_support::weights::WeightToFee;
96    use frame_system::pallet_prelude::*;
97    use frame_system::SetCode;
98    use sp_executive::{InherentError, InherentType, INHERENT_IDENTIFIER};
99
100    #[pallet::config]
101    pub trait Config: frame_system::Config {
102        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
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            <frame_system::pallet::Pallet<T>>::can_set_code(&code)?;
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                    if code != &provided_code {
164                        return Err(InherentError::IncorrectRuntimeCode);
165                    }
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            // TODO: Probably needs a different value
186            Weight::from_parts(1, 0)
187        }
188    }
189}
190
191/// Same semantics with `frame_executive::Executive`.
192///
193/// One extra generic parameter:
194/// - `ExecutiveConfig`: Something that implements `domain_pallet_executive::Config`.
195pub struct Executive<
196    ExecutiveConfig,
197    Context,
198    UnsignedValidator,
199    AllPalletsWithSystem,
200    OnRuntimeUpgrade = (),
201>(
202    PhantomData<(
203        ExecutiveConfig,
204        Context,
205        UnsignedValidator,
206        AllPalletsWithSystem,
207        OnRuntimeUpgrade,
208    )>,
209);
210
211impl<
212        ExecutiveConfig: Config + frame_system::Config + EnsureInherentsAreFirst<BlockOf<ExecutiveConfig>>,
213        Context: Default,
214        UnsignedValidator,
215        AllPalletsWithSystem: OnRuntimeUpgrade
216            + BeforeAllRuntimeMigrations
217            + OnInitialize<BlockNumberFor<ExecutiveConfig>>
218            + OnIdle<BlockNumberFor<ExecutiveConfig>>
219            + OnFinalize<BlockNumberFor<ExecutiveConfig>>
220            + OffchainWorker<BlockNumberFor<ExecutiveConfig>>
221            + OnPoll<BlockNumberFor<ExecutiveConfig>>,
222        COnRuntimeUpgrade: OnRuntimeUpgrade,
223    > ExecuteBlock<BlockOf<ExecutiveConfig>>
224    for Executive<
225        ExecutiveConfig,
226        Context,
227        UnsignedValidator,
228        AllPalletsWithSystem,
229        COnRuntimeUpgrade,
230    >
231where
232    ExtrinsicOf<ExecutiveConfig>: Checkable<Context> + Codec,
233    CheckedOf<ExtrinsicOf<ExecutiveConfig>, Context>: Applyable + GetDispatchInfo,
234    CallOf<ExtrinsicOf<ExecutiveConfig>, Context>:
235        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
236    OriginOf<ExtrinsicOf<ExecutiveConfig>, Context>: From<Option<AccountIdOf<ExecutiveConfig>>>,
237    UnsignedValidator: ValidateUnsigned<Call = CallOf<ExtrinsicOf<ExecutiveConfig>, Context>>,
238{
239    fn execute_block(block: BlockOf<ExecutiveConfig>) {
240        Executive::<
241            ExecutiveConfig,
242            Context,
243            UnsignedValidator,
244            AllPalletsWithSystem,
245            COnRuntimeUpgrade,
246        >::execute_block(block);
247    }
248}
249
250impl<
251        ExecutiveConfig: Config + frame_system::Config + EnsureInherentsAreFirst<BlockOf<ExecutiveConfig>>,
252        Context: Default,
253        UnsignedValidator,
254        AllPalletsWithSystem: OnRuntimeUpgrade
255            + BeforeAllRuntimeMigrations
256            + OnInitialize<BlockNumberFor<ExecutiveConfig>>
257            + OnIdle<BlockNumberFor<ExecutiveConfig>>
258            + OnFinalize<BlockNumberFor<ExecutiveConfig>>
259            + OffchainWorker<BlockNumberFor<ExecutiveConfig>>
260            + OnPoll<BlockNumberFor<ExecutiveConfig>>,
261        COnRuntimeUpgrade: OnRuntimeUpgrade,
262    >
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    // TODO: https://github.com/paritytech/substrate/issues/10711
305    fn initial_checks(block: &BlockOf<ExecutiveConfig>) -> u32 {
306        sp_tracing::enter_span!(sp_tracing::Level::TRACE, "initial_checks");
307        let header = block.header();
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        match ExecutiveConfig::ensure_inherents_are_first(block) {
320            Ok(num) => num,
321            Err(i) => panic!("Invalid inherent position for extrinsic at index {}", i),
322        }
323    }
324
325    /// Wrapped `frame_executive::Executive::execute_block`.
326    ///
327    /// The purpose is to use our custom [`Executive::apply_extrinsic`] and
328    /// the [`Executive::finalize_block`] logic.
329    pub fn execute_block(block: BlockOf<ExecutiveConfig>) {
330        sp_io::init_tracing();
331        sp_tracing::within_span! {
332            sp_tracing::info_span!("execute_block", ?block);
333            // Execute `on_runtime_upgrade` and `on_initialize`.
334            let mode = Self::initialize_block(block.header());
335            let num_inherents = Self::initial_checks(&block) as usize;
336            let (header, extrinsics) = block.deconstruct();
337            let num_extrinsics = extrinsics.len();
338
339            if mode == ExtrinsicInclusionMode::OnlyInherents && num_extrinsics > num_inherents {
340                // Invalid block
341                panic!("Only inherents are allowed in this block")
342            }
343
344            Self::apply_extrinsics(extrinsics.into_iter());
345
346            // In this case there were no transactions to trigger this state transition:
347            if !<frame_system::Pallet<ExecutiveConfig>>::inherents_applied() {
348                defensive_assert!(num_inherents == num_extrinsics);
349                frame_executive::Executive::<
350                    ExecutiveConfig,
351                    BlockOf<ExecutiveConfig>,
352                    Context,
353                    UnsignedValidator,
354                    AllPalletsWithSystem,
355                    COnRuntimeUpgrade,
356                >::inherents_applied();
357            }
358
359            <frame_system::Pallet<ExecutiveConfig>>::note_finished_extrinsics();
360            <ExecutiveConfig as frame_system::Config>::PostTransactions::post_transactions();
361
362            Self::idle_and_finalize_hook(*header.number());
363            Self::final_checks(&header);
364        }
365    }
366
367    /// Wrapped `frame_executive::Executive::apply_extrinsics`.
368    fn apply_extrinsics(extrinsics: impl Iterator<Item = ExtrinsicOf<ExecutiveConfig>>) {
369        extrinsics.into_iter().for_each(|e| {
370            if let Err(e) = Self::apply_extrinsic(e) {
371                let err: &'static str = e.into();
372                panic!("{}", err)
373            }
374        });
375    }
376
377    /// Wrapped `frame_executive::Executive::finalize_block`.
378    pub fn finalize_block() -> HeaderFor<ExecutiveConfig> {
379        frame_executive::Executive::<
380            ExecutiveConfig,
381            BlockOf<ExecutiveConfig>,
382            Context,
383            UnsignedValidator,
384            AllPalletsWithSystem,
385            COnRuntimeUpgrade,
386        >::finalize_block()
387    }
388
389    /// Wrapped `frame_executive::Executive::on_idle_hook` and `frame_executive::Executive::on_finalize_hook`
390    fn idle_and_finalize_hook(block_number: NumberFor<BlockOf<ExecutiveConfig>>) {
391        let weight = <frame_system::Pallet<ExecutiveConfig>>::block_weight();
392        let max_weight = <<ExecutiveConfig as frame_system::Config>::BlockWeights as frame_support::traits::Get<_>>::get().max_block;
393        let remaining_weight = max_weight.saturating_sub(weight.total());
394
395        if remaining_weight.all_gt(Weight::zero()) {
396            let used_weight = AllPalletsWithSystem::on_idle(block_number, remaining_weight);
397            <frame_system::Pallet<ExecutiveConfig>>::register_extra_weight_unchecked(
398                used_weight,
399                DispatchClass::Mandatory,
400            );
401        }
402
403        AllPalletsWithSystem::on_finalize(block_number);
404    }
405
406    /// Wrapped `frame_executive::Executive::apply_extrinsic`.
407    ///
408    /// Note the storage root in the beginning.
409    pub fn apply_extrinsic(uxt: ExtrinsicOf<ExecutiveConfig>) -> ApplyExtrinsicResult {
410        // apply the extrinsic within another transaction so that changes can be reverted.
411        let res = with_storage_layer(|| {
412            frame_executive::Executive::<
413                ExecutiveConfig,
414                BlockOf<ExecutiveConfig>,
415                Context,
416                UnsignedValidator,
417                AllPalletsWithSystem,
418                COnRuntimeUpgrade,
419            >::apply_extrinsic(uxt.clone())
420            .map_err(|err| DispatchError::Other(err.into()))
421        });
422
423        // apply extrinsic failed with transaction validity error
424        // this could happen for following scenarios
425        // - Bad extrinsic Signature
426        //      This extrinsic will be ignored by the operators during the bundle check
427        //      and marks such bundle as Invalid.
428        // - Extrinsic execution failed
429        //      There are multiple scenarios why this can happen
430        //      - Inherent extrinsic failed. If this happens, we should fail to apply extrinsic
431        //      - Pre and Post dispatch fails. Check the test `test_domain_block_builder_include_ext_with_failed_predispatch`
432        //        why this could happen. If it fail due to this, then we revert the inner storage changes
433        //        but still include extrinsic so that we can clear inconsistency between block body and trace roots.
434        match res {
435            Ok(dispatch_outcome) => Ok(dispatch_outcome),
436            Err(err) => {
437                let encoded = uxt.encode();
438                let (maybe_signer, dispatch_info) =
439                    ExecutiveConfig::ExtrinsicStorageFees::extract_signer(uxt);
440                // if this is mandatory extrinsic, then transaction should not execute
441                // we should fail here.
442                if dispatch_info.class == DispatchClass::Mandatory {
443                    return Err(TransactionValidityError::Invalid(
444                        InvalidTransaction::MandatoryValidation,
445                    ));
446                }
447
448                // charge signer for extrinsic storage
449                if let Some(signer) = maybe_signer {
450                    let storage_fees = ExecutiveConfig::LengthToFee::weight_to_fee(
451                        &Weight::from_parts(encoded.len() as u64, 0),
452                    );
453
454                    // best effort to charge the fees to signer.
455                    // if signer does not have enough balance, we continue
456                    let maybe_charged_fees = ExecutiveConfig::Currency::burn_from(
457                        &signer,
458                        storage_fees,
459                        Preservation::Expendable,
460                        Precision::BestEffort,
461                        Fortitude::Force,
462                    );
463
464                    if let Ok(charged_fees) = maybe_charged_fees {
465                        ExecutiveConfig::ExtrinsicStorageFees::on_storage_fees_charged(
466                            charged_fees,
467                            encoded.len() as u32,
468                        )?;
469                    }
470                }
471
472                // note the extrinsic into system storage
473                <frame_system::Pallet<ExecutiveConfig>>::note_extrinsic(encoded);
474
475                // note extrinsic applied. it pays no fees since there was no execution.
476                // we also set the weight to zero since there was no execution done.
477                let r = Err(DispatchErrorWithPostInfo {
478                    post_info: PostDispatchInfo {
479                        actual_weight: None,
480                        pays_fee: Pays::No,
481                    },
482                    error: err,
483                });
484
485                <frame_system::Pallet<ExecutiveConfig>>::note_applied_extrinsic(&r, dispatch_info);
486                Ok(Err(err))
487            }
488        }
489    }
490
491    // TODO: https://github.com/paritytech/substrate/issues/10711
492    fn final_checks(header: &HeaderFor<ExecutiveConfig>) {
493        sp_tracing::enter_span!(sp_tracing::Level::TRACE, "final_checks");
494        // remove temporaries
495        let new_header = <frame_system::Pallet<ExecutiveConfig>>::finalize();
496
497        // check digest
498        assert_eq!(
499            header.digest().logs().len(),
500            new_header.digest().logs().len(),
501            "Number of digest items must match that calculated."
502        );
503        let items_zip = header
504            .digest()
505            .logs()
506            .iter()
507            .zip(new_header.digest().logs().iter());
508        for (header_item, computed_item) in items_zip {
509            header_item.check_equal(computed_item);
510            assert_eq!(
511                header_item, computed_item,
512                "Digest item must match that calculated."
513            );
514        }
515
516        // check storage root.
517        let storage_root = new_header.state_root();
518        header.state_root().check_equal(storage_root);
519        assert!(
520            header.state_root() == storage_root,
521            "Storage root must match that calculated."
522        );
523
524        assert!(
525            header.extrinsics_root() == new_header.extrinsics_root(),
526            "Transaction trie root must be valid.",
527        );
528    }
529
530    /// Wrapped `frame_executive::Executive::validate_transaction`.
531    pub fn validate_transaction(
532        source: TransactionSource,
533        uxt: ExtrinsicOf<ExecutiveConfig>,
534        block_hash: BlockHashOf<ExecutiveConfig>,
535    ) -> TransactionValidity {
536        frame_executive::Executive::<
537            ExecutiveConfig,
538            BlockOf<ExecutiveConfig>,
539            Context,
540            UnsignedValidator,
541            AllPalletsWithSystem,
542            COnRuntimeUpgrade,
543        >::validate_transaction(source, uxt, block_hash)
544    }
545
546    /// Wrapped `frame_executive::Executive::offchain_worker`.
547    pub fn offchain_worker(header: &HeaderFor<ExecutiveConfig>) {
548        frame_executive::Executive::<
549            ExecutiveConfig,
550            BlockOf<ExecutiveConfig>,
551            Context,
552            UnsignedValidator,
553            AllPalletsWithSystem,
554            COnRuntimeUpgrade,
555        >::offchain_worker(header)
556    }
557}