From 15e33c46f3b9a3669e85538021327f868ce8eed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= <bkchr@users.noreply.github.com> Date: Sat, 25 Apr 2020 17:59:58 +0200 Subject: [PATCH] Add support for custom runtime upgrade logic (#5782) This adds support for registering custom runtime upgrade logic that can be implemented in the runtime directly instead of putting it into a module. --- substrate/frame/executive/src/lib.rs | 125 ++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 12 deletions(-) diff --git a/substrate/frame/executive/src/lib.rs b/substrate/frame/executive/src/lib.rs index e66fdff597e..742397b6247 100644 --- a/substrate/frame/executive/src/lib.rs +++ b/substrate/frame/executive/src/lib.rs @@ -60,19 +60,57 @@ //! # pub type AllModules = u64; //! # pub enum Runtime {}; //! # use sp_runtime::transaction_validity::{ -//! TransactionValidity, UnknownTransaction, TransactionSource, +//! # TransactionValidity, UnknownTransaction, TransactionSource, //! # }; //! # use sp_runtime::traits::ValidateUnsigned; //! # impl ValidateUnsigned for Runtime { -//! # type Call = (); +//! # type Call = (); //! # -//! # fn validate_unsigned(_source: TransactionSource, _call: &Self::Call) -> TransactionValidity { -//! # UnknownTransaction::NoUnsignedValidator.into() -//! # } +//! # fn validate_unsigned(_source: TransactionSource, _call: &Self::Call) -> TransactionValidity { +//! # UnknownTransaction::NoUnsignedValidator.into() +//! # } //! # } //! /// Executive: handles dispatch to the various modules. //! pub type Executive = executive::Executive<Runtime, Block, Context, Runtime, AllModules>; //! ``` +//! +//! ### Custom `OnRuntimeUpgrade` logic +//! +//! You can add custom logic that should be called in your runtime on a runtime upgrade. This is +//! done by setting an optional generic parameter. The custom logic will be called before +//! the on runtime upgrade logic of all modules is called. +//! +//! ``` +//! # use sp_runtime::generic; +//! # use frame_executive as executive; +//! # pub struct UncheckedExtrinsic {}; +//! # pub struct Header {}; +//! # type Context = frame_system::ChainContext<Runtime>; +//! # pub type Block = generic::Block<Header, UncheckedExtrinsic>; +//! # pub type Balances = u64; +//! # pub type AllModules = u64; +//! # pub enum Runtime {}; +//! # use sp_runtime::transaction_validity::{ +//! # TransactionValidity, UnknownTransaction, TransactionSource, +//! # }; +//! # use sp_runtime::traits::ValidateUnsigned; +//! # impl ValidateUnsigned for Runtime { +//! # type Call = (); +//! # +//! # fn validate_unsigned(_source: TransactionSource, _call: &Self::Call) -> TransactionValidity { +//! # UnknownTransaction::NoUnsignedValidator.into() +//! # } +//! # } +//! struct CustomOnRuntimeUpgrade; +//! impl frame_support::traits::OnRuntimeUpgrade for CustomOnRuntimeUpgrade { +//! fn on_runtime_upgrade() -> frame_support::weights::Weight { +//! // Do whatever you want. +//! 0 +//! } +//! } +//! +//! pub type Executive = executive::Executive<Runtime, Block, Context, Runtime, AllModules, CustomOnRuntimeUpgrade>; +//! ``` #![cfg_attr(not(feature = "std"), no_std)] @@ -102,8 +140,19 @@ pub type CheckedOf<E, C> = <E as Checkable<C>>::Checked; pub type CallOf<E, C> = <CheckedOf<E, C> as Applyable>::Call; pub type OriginOf<E, C> = <CallOf<E, C> as Dispatchable>::Origin; -pub struct Executive<System, Block, Context, UnsignedValidator, AllModules>( - PhantomData<(System, Block, Context, UnsignedValidator, AllModules)> +/// Main entry point for certain runtime actions as e.g. `execute_block`. +/// +/// Generic parameters: +/// - `System`: Something that implements `frame_system::Trait` +/// - `Block`: The block type of the runtime +/// - `Context`: The context that is used when checking an extrinsic. +/// - `UnsignedValidator`: The unsigned transaction validator of the runtime. +/// - `AllModules`: Tuple that contains all modules. Will be used to call e.g. `on_initialize`. +/// - `OnRuntimeUpgrade`: Custom logic that should be called after a runtime upgrade. Modules are +/// already called by `AllModules`. It will be called before all modules will +/// be called. +pub struct Executive<System, Block, Context, UnsignedValidator, AllModules, OnRuntimeUpgrade = ()>( + PhantomData<(System, Block, Context, UnsignedValidator, AllModules, OnRuntimeUpgrade)> ); impl< @@ -116,7 +165,9 @@ impl< OnInitialize<System::BlockNumber> + OnFinalize<System::BlockNumber> + OffchainWorker<System::BlockNumber>, -> ExecuteBlock<Block> for Executive<System, Block, Context, UnsignedValidator, AllModules> + COnRuntimeUpgrade: OnRuntimeUpgrade, +> ExecuteBlock<Block> for + Executive<System, Block, Context, UnsignedValidator, AllModules, COnRuntimeUpgrade> where Block::Extrinsic: Checkable<Context> + Codec, CheckedOf<Block::Extrinsic, Context>: @@ -141,7 +192,8 @@ impl< OnInitialize<System::BlockNumber> + OnFinalize<System::BlockNumber> + OffchainWorker<System::BlockNumber>, -> Executive<System, Block, Context, UnsignedValidator, AllModules> + COnRuntimeUpgrade: OnRuntimeUpgrade, +> Executive<System, Block, Context, UnsignedValidator, AllModules, COnRuntimeUpgrade> where Block::Extrinsic: Checkable<Context> + Codec, CheckedOf<Block::Extrinsic, Context>: @@ -180,8 +232,9 @@ where ) { if Self::runtime_upgraded() { // System is not part of `AllModules`, so we need to call this manually. - <frame_system::Module::<System> as OnRuntimeUpgrade>::on_runtime_upgrade(); - let weight = <AllModules as OnRuntimeUpgrade>::on_runtime_upgrade(); + let mut weight = <frame_system::Module::<System> as OnRuntimeUpgrade>::on_runtime_upgrade(); + weight = weight.saturating_add(COnRuntimeUpgrade::on_runtime_upgrade()); + weight = weight.saturating_add(<AllModules as OnRuntimeUpgrade>::on_runtime_upgrade()); <frame_system::Module<System>>::register_extra_weight_unchecked(weight); } <frame_system::Module<System>>::initialize( @@ -410,6 +463,7 @@ mod tests { use frame_system::{self as system, Call as SystemCall, ChainContext, LastRuntimeUpgradeInfo}; use pallet_balances::Call as BalancesCall; use hex_literal::hex; + const TEST_KEY: &[u8] = &*b":test:key:"; mod custom { use frame_support::weights::{Weight, DispatchClass}; @@ -442,6 +496,11 @@ mod tests { fn on_finalize() { println!("on_finalize(?)"); } + + fn on_runtime_upgrade() -> Weight { + sp_io::storage::set(super::TEST_KEY, "module".as_bytes()); + 0 + } } } } @@ -570,7 +629,27 @@ mod tests { ); type AllModules = (System, Balances, Custom); type TestXt = sp_runtime::testing::TestXt<Call, SignedExtra>; - type Executive = super::Executive<Runtime, Block<TestXt>, ChainContext<Runtime>, Runtime, AllModules>; + + // Will contain `true` when the custom runtime logic was called. + const CUSTOM_ON_RUNTIME_KEY: &[u8] = &*b":custom:on_runtime"; + + struct CustomOnRuntimeUpgrade; + impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade { + fn on_runtime_upgrade() -> Weight { + sp_io::storage::set(TEST_KEY, "custom_upgrade".as_bytes()); + sp_io::storage::set(CUSTOM_ON_RUNTIME_KEY, &true.encode()); + 0 + } + } + + type Executive = super::Executive< + Runtime, + Block<TestXt>, + ChainContext<Runtime>, + Runtime, + AllModules, + CustomOnRuntimeUpgrade + >; fn extra(nonce: u64, fee: Balance) -> SignedExtra { ( @@ -899,4 +978,26 @@ mod tests { assert_eq!(result, last.was_upgraded(¤t)); } } + + #[test] + fn custom_runtime_upgrade_is_called_before_modules() { + new_test_ext(1).execute_with(|| { + // Make sure `on_runtime_upgrade` is called. + RUNTIME_VERSION.with(|v| *v.borrow_mut() = sp_version::RuntimeVersion { + spec_version: 1, + ..Default::default() + }); + + Executive::initialize_block(&Header::new( + 1, + H256::default(), + H256::default(), + [69u8; 32].into(), + Digest::default(), + )); + + assert_eq!(&sp_io::storage::get(TEST_KEY).unwrap()[..], *b"module"); + assert_eq!(sp_io::storage::get(CUSTOM_ON_RUNTIME_KEY).unwrap(), true.encode()); + }); + } } -- GitLab