mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 02:48:03 +00:00
Add Authorize Upgrade Pattern to Frame System (#2682)
Adds the `authorize_upgrade` -> `enact_authorized_upgrade` pattern to `frame-system`. This will be useful for upgrading bridged chains that are under the governance of Polkadot without passing entire runtime Wasm blobs over a bridge. Notes: - Changed `enact_authorized_upgrade` to `apply_authorized_upgrade`. Personal opinion, "apply" more accurately expresses what it's doing. Can change back if outvoted. - Remove `check_version` in favor of two extrinsics, so as to make _checked_ the default. - Left calls in `parachain-system` and marked as deprecated to prevent breaking the API. They just call into the `frame-system` functions. - Updated `frame-system` benchmarks to v2 syntax. --------- Co-authored-by: command-bot <>
This commit is contained in:
@@ -17,27 +17,60 @@
|
||||
|
||||
//! # System Pallet
|
||||
//!
|
||||
//! The System pallet provides low-level access to core types and cross-cutting utilities.
|
||||
//! It acts as the base layer for other pallets to interact with the Substrate framework components.
|
||||
//! The System pallet provides low-level access to core types and cross-cutting utilities. It acts
|
||||
//! as the base layer for other pallets to interact with the Substrate framework components.
|
||||
//!
|
||||
//! - [`Config`]
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! The System pallet defines the core data types used in a Substrate runtime.
|
||||
//! It also provides several utility functions (see [`Pallet`]) for other FRAME pallets.
|
||||
//! The System pallet defines the core data types used in a Substrate runtime. It also provides
|
||||
//! several utility functions (see [`Pallet`]) for other FRAME pallets.
|
||||
//!
|
||||
//! In addition, it manages the storage items for extrinsics data, indexes, event records, and
|
||||
//! digest items, among other things that support the execution of the current block.
|
||||
//! In addition, it manages the storage items for extrinsic data, indices, event records, and digest
|
||||
//! items, among other things that support the execution of the current block.
|
||||
//!
|
||||
//! It also handles low-level tasks like depositing logs, basic set up and take down of
|
||||
//! temporary storage entries, and access to previous block hashes.
|
||||
//! It also handles low-level tasks like depositing logs, basic set up and take down of temporary
|
||||
//! storage entries, and access to previous block hashes.
|
||||
//!
|
||||
//! ## Interface
|
||||
//!
|
||||
//! ### Dispatchable Functions
|
||||
//!
|
||||
//! The System pallet does not implement any dispatchable functions.
|
||||
//! The System pallet provides dispatchable functions that, with the exception of `remark`, manage
|
||||
//! low-level or privileged functionality of a Substrate-based runtime.
|
||||
//!
|
||||
//! - `remark`: Make some on-chain remark.
|
||||
//! - `set_heap_pages`: Set the number of pages in the WebAssembly environment's heap.
|
||||
//! - `set_code`: Set the new runtime code.
|
||||
//! - `set_code_without_checks`: Set the new runtime code without any checks.
|
||||
//! - `set_storage`: Set some items of storage.
|
||||
//! - `kill_storage`: Kill some items from storage.
|
||||
//! - `kill_prefix`: Kill all storage items with a key that starts with the given prefix.
|
||||
//! - `remark_with_event`: Make some on-chain remark and emit an event.
|
||||
//! - `do_task`: Do some specified task.
|
||||
//! - `authorize_upgrade`: Authorize new runtime code.
|
||||
//! - `authorize_upgrade_without_checks`: Authorize new runtime code and an upgrade sans
|
||||
//! verification.
|
||||
//! - `apply_authorized_upgrade`: Provide new, already-authorized runtime code.
|
||||
//!
|
||||
//! #### A Note on Upgrades
|
||||
//!
|
||||
//! The pallet provides two primary means of upgrading the runtime, a single-phase means using
|
||||
//! `set_code` and a two-phase means using `authorize_upgrade` followed by
|
||||
//! `apply_authorized_upgrade`. The first will directly attempt to apply the provided `code`
|
||||
//! (application may have to be scheduled, depending on the context and implementation of the
|
||||
//! `OnSetCode` trait).
|
||||
//!
|
||||
//! The `authorize_upgrade` route allows the authorization of a runtime's code hash. Once
|
||||
//! authorized, anyone may upload the correct runtime to apply the code. This pattern is useful when
|
||||
//! providing the runtime ahead of time may be unwieldy, for example when a large preimage (the
|
||||
//! code) would need to be stored on-chain or sent over a message transport protocol such as a
|
||||
//! bridge.
|
||||
//!
|
||||
//! The `*_without_checks` variants do not perform any version checks, so using them runs the risk
|
||||
//! of applying a downgrade or entirely other chain specification. They will still validate that the
|
||||
//! `code` meets the authorized hash.
|
||||
//!
|
||||
//! ### Public Functions
|
||||
//!
|
||||
@@ -59,7 +92,7 @@
|
||||
//! - [`CheckTxVersion`]: Checks that the transaction version is the same as the one used to sign
|
||||
//! the transaction.
|
||||
//!
|
||||
//! Lookup the runtime aggregator file (e.g. `node/runtime`) to see the full list of signed
|
||||
//! Look up the runtime aggregator file (e.g. `node/runtime`) to see the full list of signed
|
||||
//! extensions included in a chain.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
@@ -77,6 +110,10 @@ use sp_runtime::{
|
||||
Hash, Header, Lookup, LookupError, MaybeDisplay, MaybeSerializeDeserialize, Member, One,
|
||||
Saturating, SimpleBitOps, StaticLookup, Zero,
|
||||
},
|
||||
transaction_validity::{
|
||||
InvalidTransaction, TransactionLongevity, TransactionSource, TransactionValidity,
|
||||
ValidTransaction,
|
||||
},
|
||||
DispatchError, RuntimeDebug,
|
||||
};
|
||||
#[cfg(any(feature = "std", test))]
|
||||
@@ -90,9 +127,10 @@ use frame_support::traits::BuildGenesisConfig;
|
||||
use frame_support::{
|
||||
dispatch::{
|
||||
extract_actual_pays_fee, extract_actual_weight, DispatchClass, DispatchInfo,
|
||||
DispatchResult, DispatchResultWithPostInfo, PerDispatchClass,
|
||||
DispatchResult, DispatchResultWithPostInfo, PerDispatchClass, PostDispatchInfo,
|
||||
},
|
||||
impl_ensure_origin_with_arg_ignoring_arg,
|
||||
ensure, impl_ensure_origin_with_arg_ignoring_arg,
|
||||
pallet_prelude::Pays,
|
||||
storage::{self, StorageStreamIter},
|
||||
traits::{
|
||||
ConstU32, Contains, EnsureOrigin, EnsureOriginWithArg, Get, HandleLifetime,
|
||||
@@ -198,6 +236,20 @@ impl<MaxNormal: Get<u32>, MaxOverflow: Get<u32>> ConsumerLimits for (MaxNormal,
|
||||
}
|
||||
}
|
||||
|
||||
/// Information needed when a new runtime binary is submitted and needs to be authorized before
|
||||
/// replacing the current runtime.
|
||||
#[derive(Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CodeUpgradeAuthorization<T>
|
||||
where
|
||||
T: Config,
|
||||
{
|
||||
/// Hash of the new runtime binary.
|
||||
code_hash: T::Hash,
|
||||
/// Whether or not to carry out version checks.
|
||||
check_version: bool,
|
||||
}
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use crate::{self as frame_system, pallet_prelude::*, *};
|
||||
@@ -661,6 +713,56 @@ pub mod pallet {
|
||||
// Return success.
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
/// Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied
|
||||
/// later.
|
||||
///
|
||||
/// This call requires Root origin.
|
||||
#[pallet::call_index(9)]
|
||||
#[pallet::weight((T::SystemWeightInfo::authorize_upgrade(), DispatchClass::Operational))]
|
||||
pub fn authorize_upgrade(origin: OriginFor<T>, code_hash: T::Hash) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
Self::do_authorize_upgrade(code_hash, true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied
|
||||
/// later.
|
||||
///
|
||||
/// WARNING: This authorizes an upgrade that will take place without any safety checks, for
|
||||
/// example that the spec name remains the same and that the version number increases. Not
|
||||
/// recommended for normal use. Use `authorize_upgrade` instead.
|
||||
///
|
||||
/// This call requires Root origin.
|
||||
#[pallet::call_index(10)]
|
||||
#[pallet::weight((T::SystemWeightInfo::authorize_upgrade(), DispatchClass::Operational))]
|
||||
pub fn authorize_upgrade_without_checks(
|
||||
origin: OriginFor<T>,
|
||||
code_hash: T::Hash,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
Self::do_authorize_upgrade(code_hash, false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Provide the preimage (runtime binary) `code` for an upgrade that has been authorized.
|
||||
///
|
||||
/// If the authorization required a version check, this call will ensure the spec name
|
||||
/// remains unchanged and that the spec version has increased.
|
||||
///
|
||||
/// Depending on the runtime's `OnSetCode` configuration, this function may directly apply
|
||||
/// the new `code` in the same block or attempt to schedule the upgrade.
|
||||
///
|
||||
/// All origins are allowed.
|
||||
#[pallet::call_index(11)]
|
||||
#[pallet::weight((T::SystemWeightInfo::apply_authorized_upgrade(), DispatchClass::Operational))]
|
||||
pub fn apply_authorized_upgrade(
|
||||
_: OriginFor<T>,
|
||||
code: Vec<u8>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let post = Self::do_apply_authorize_upgrade(code)?;
|
||||
Ok(post)
|
||||
}
|
||||
}
|
||||
|
||||
/// Event for the System pallet.
|
||||
@@ -687,6 +789,8 @@ pub mod pallet {
|
||||
#[cfg(feature = "experimental")]
|
||||
/// A [`Task`] failed during execution.
|
||||
TaskFailed { task: T::RuntimeTask, err: DispatchError },
|
||||
/// An upgrade was authorized.
|
||||
UpgradeAuthorized { code_hash: T::Hash, check_version: bool },
|
||||
}
|
||||
|
||||
/// Error for the System pallet
|
||||
@@ -714,6 +818,10 @@ pub mod pallet {
|
||||
#[cfg(feature = "experimental")]
|
||||
/// The specified [`Task`] failed during execution.
|
||||
FailedTask,
|
||||
/// No upgrade authorized.
|
||||
NothingAuthorized,
|
||||
/// The submitted code is not authorized.
|
||||
Unauthorized,
|
||||
}
|
||||
|
||||
/// Exposed trait-generic origin type.
|
||||
@@ -829,6 +937,12 @@ pub mod pallet {
|
||||
#[pallet::whitelist_storage]
|
||||
pub(super) type ExecutionPhase<T: Config> = StorageValue<_, Phase>;
|
||||
|
||||
/// `Some` if a code upgrade has been authorized.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn authorized_upgrade)]
|
||||
pub(super) type AuthorizedUpgrade<T: Config> =
|
||||
StorageValue<_, CodeUpgradeAuthorization<T>, OptionQuery>;
|
||||
|
||||
#[derive(frame_support::DefaultNoBound)]
|
||||
#[pallet::genesis_config]
|
||||
pub struct GenesisConfig<T: Config> {
|
||||
@@ -848,6 +962,25 @@ pub mod pallet {
|
||||
sp_io::storage::set(well_known_keys::EXTRINSIC_INDEX, &0u32.encode());
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::validate_unsigned]
|
||||
impl<T: Config> sp_runtime::traits::ValidateUnsigned for Pallet<T> {
|
||||
type Call = Call<T>;
|
||||
fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
|
||||
if let Call::apply_authorized_upgrade { ref code } = call {
|
||||
if let Ok(hash) = Self::validate_authorized_upgrade(&code[..]) {
|
||||
return Ok(ValidTransaction {
|
||||
priority: 100,
|
||||
requires: Vec::new(),
|
||||
provides: vec![hash.as_ref().to_vec()],
|
||||
longevity: TransactionLongevity::max_value(),
|
||||
propagate: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(InvalidTransaction::Call.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Key = Vec<u8>;
|
||||
@@ -1872,6 +2005,41 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// To be called after any origin/privilege checks. Put the code upgrade authorization into
|
||||
/// storage and emit an event. Infallible.
|
||||
pub fn do_authorize_upgrade(code_hash: T::Hash, check_version: bool) {
|
||||
AuthorizedUpgrade::<T>::put(CodeUpgradeAuthorization { code_hash, check_version });
|
||||
Self::deposit_event(Event::UpgradeAuthorized { code_hash, check_version });
|
||||
}
|
||||
|
||||
/// Apply an authorized upgrade, performing any validation checks, and remove the authorization.
|
||||
/// Whether or not the code is set directly depends on the `OnSetCode` configuration of the
|
||||
/// runtime.
|
||||
pub fn do_apply_authorize_upgrade(code: Vec<u8>) -> Result<PostDispatchInfo, DispatchError> {
|
||||
Self::validate_authorized_upgrade(&code[..])?;
|
||||
T::OnSetCode::set_code(code)?;
|
||||
AuthorizedUpgrade::<T>::kill();
|
||||
let post = PostDispatchInfo {
|
||||
// consume the rest of the block to prevent further transactions
|
||||
actual_weight: Some(T::BlockWeights::get().max_block),
|
||||
// no fee for valid upgrade
|
||||
pays_fee: Pays::No,
|
||||
};
|
||||
Ok(post)
|
||||
}
|
||||
|
||||
/// Check that provided `code` can be upgraded to. Namely, check that its hash matches an
|
||||
/// existing authorization and that it meets the specification requirements of `can_set_code`.
|
||||
pub fn validate_authorized_upgrade(code: &[u8]) -> Result<T::Hash, DispatchError> {
|
||||
let authorization = AuthorizedUpgrade::<T>::get().ok_or(Error::<T>::NothingAuthorized)?;
|
||||
let actual_hash = T::Hashing::hash(code);
|
||||
ensure!(actual_hash == authorization.code_hash, Error::<T>::Unauthorized);
|
||||
if authorization.check_version {
|
||||
Self::can_set_code(code)?
|
||||
}
|
||||
Ok(actual_hash)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a 32 byte datum which is guaranteed to be universally unique. `entropy` is provided
|
||||
|
||||
Reference in New Issue
Block a user