mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-10 04:07:59 +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:
@@ -27,7 +27,7 @@
|
||||
//!
|
||||
//! Users must ensure that they register this pallet as an inherent provider.
|
||||
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use codec::{Decode, Encode};
|
||||
use cumulus_primitives_core::{
|
||||
relay_chain, AbridgedHostConfiguration, ChannelInfo, ChannelStatus, CollationInfo,
|
||||
GetChannelInfo, InboundDownwardMessage, InboundHrmpMessage, MessageSendError,
|
||||
@@ -50,10 +50,9 @@ use scale_info::TypeInfo;
|
||||
use sp_runtime::{
|
||||
traits::{Block as BlockT, BlockNumberProvider, Hash},
|
||||
transaction_validity::{
|
||||
InvalidTransaction, TransactionLongevity, TransactionSource, TransactionValidity,
|
||||
ValidTransaction,
|
||||
InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction,
|
||||
},
|
||||
BoundedSlice, DispatchError, FixedU128, RuntimeDebug, Saturating,
|
||||
BoundedSlice, FixedU128, RuntimeDebug, Saturating,
|
||||
};
|
||||
use sp_std::{cmp, collections::btree_map::BTreeMap, prelude::*};
|
||||
use xcm::latest::XcmHash;
|
||||
@@ -169,20 +168,6 @@ impl CheckAssociatedRelayNumber for RelayNumberMonotonicallyIncreases {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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))]
|
||||
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,
|
||||
}
|
||||
|
||||
/// The max length of a DMP message.
|
||||
pub type MaxDmpMessageLenOf<T> = <<T as Config>::DmpQueue as HandleMessage>::MaxMessageLen;
|
||||
|
||||
@@ -204,7 +189,7 @@ pub mod ump_constants {
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use frame_system::{pallet_prelude::*, WeightInfo as SystemWeightInfo};
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::storage_version(migration::STORAGE_VERSION)]
|
||||
@@ -677,16 +662,18 @@ pub mod pallet {
|
||||
///
|
||||
/// This call requires Root origin.
|
||||
#[pallet::call_index(2)]
|
||||
#[pallet::weight((1_000_000, DispatchClass::Operational))]
|
||||
#[pallet::weight(<T as frame_system::Config>::SystemWeightInfo::authorize_upgrade())]
|
||||
#[allow(deprecated)]
|
||||
#[deprecated(
|
||||
note = "To be removed after June 2024. Migrate to `frame_system::authorize_upgrade`."
|
||||
)]
|
||||
pub fn authorize_upgrade(
|
||||
origin: OriginFor<T>,
|
||||
code_hash: T::Hash,
|
||||
check_version: bool,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
AuthorizedUpgrade::<T>::put(CodeUpgradeAuthorization { code_hash, check_version });
|
||||
|
||||
Self::deposit_event(Event::UpgradeAuthorized { code_hash });
|
||||
frame_system::Pallet::<T>::do_authorize_upgrade(code_hash, check_version);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -700,15 +687,17 @@ pub mod pallet {
|
||||
///
|
||||
/// All origins are allowed.
|
||||
#[pallet::call_index(3)]
|
||||
#[pallet::weight({1_000_000})]
|
||||
#[pallet::weight(<T as frame_system::Config>::SystemWeightInfo::apply_authorized_upgrade())]
|
||||
#[allow(deprecated)]
|
||||
#[deprecated(
|
||||
note = "To be removed after June 2024. Migrate to `frame_system::apply_authorized_upgrade`."
|
||||
)]
|
||||
pub fn enact_authorized_upgrade(
|
||||
_: OriginFor<T>,
|
||||
code: Vec<u8>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
Self::validate_authorized_upgrade(&code[..])?;
|
||||
Self::schedule_code_upgrade(code)?;
|
||||
AuthorizedUpgrade::<T>::kill();
|
||||
Ok(Pays::No.into())
|
||||
let post = frame_system::Pallet::<T>::do_apply_authorize_upgrade(code)?;
|
||||
Ok(post)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,8 +710,6 @@ pub mod pallet {
|
||||
ValidationFunctionApplied { relay_chain_block_num: RelayChainBlockNumber },
|
||||
/// The relay-chain aborted the upgrade process.
|
||||
ValidationFunctionDiscarded,
|
||||
/// An upgrade has been authorized.
|
||||
UpgradeAuthorized { code_hash: T::Hash },
|
||||
/// Some downward messages have been received and will be processed.
|
||||
DownwardMessagesReceived { count: u32 },
|
||||
/// Downward messages were processed using the given weight.
|
||||
@@ -928,10 +915,6 @@ pub mod pallet {
|
||||
#[pallet::storage]
|
||||
pub(super) type ReservedDmpWeightOverride<T: Config> = StorageValue<_, Weight>;
|
||||
|
||||
/// The next authorized upgrade, if there is one.
|
||||
#[pallet::storage]
|
||||
pub(super) type AuthorizedUpgrade<T: Config> = StorageValue<_, CodeUpgradeAuthorization<T>>;
|
||||
|
||||
/// A custom head data that should be returned as result of `validate_block`.
|
||||
///
|
||||
/// See `Pallet::set_custom_validation_head_data` for more information.
|
||||
@@ -982,7 +965,8 @@ pub mod pallet {
|
||||
|
||||
fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
|
||||
if let Call::enact_authorized_upgrade { ref code } = call {
|
||||
if let Ok(hash) = Self::validate_authorized_upgrade(code) {
|
||||
if let Ok(hash) = frame_system::Pallet::<T>::validate_authorized_upgrade(&code[..])
|
||||
{
|
||||
return Ok(ValidTransaction {
|
||||
priority: 100,
|
||||
requires: Vec::new(),
|
||||
@@ -1001,21 +985,6 @@ pub mod pallet {
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
fn validate_authorized_upgrade(code: &[u8]) -> Result<T::Hash, DispatchError> {
|
||||
let authorization = AuthorizedUpgrade::<T>::get().ok_or(Error::<T>::NothingAuthorized)?;
|
||||
|
||||
// ensure that the actual hash matches the authorized hash
|
||||
let actual_hash = T::Hashing::hash(code);
|
||||
ensure!(actual_hash == authorization.code_hash, Error::<T>::Unauthorized);
|
||||
|
||||
// check versions if required as part of the authorization
|
||||
if authorization.check_version {
|
||||
frame_system::Pallet::<T>::can_set_code(code)?;
|
||||
}
|
||||
|
||||
Ok(actual_hash)
|
||||
}
|
||||
|
||||
/// Get the unincluded segment size after the given hash.
|
||||
///
|
||||
/// If the unincluded segment doesn't contain the given hash, this returns the
|
||||
@@ -1563,8 +1532,8 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Type that implements `SetCode`.
|
||||
pub struct ParachainSetCode<T>(sp_std::marker::PhantomData<T>);
|
||||
|
||||
impl<T: Config> frame_system::SetCode<T> for ParachainSetCode<T> {
|
||||
fn set_code(code: Vec<u8>) -> DispatchResult {
|
||||
Pallet::<T>::schedule_code_upgrade(code)
|
||||
|
||||
@@ -1127,8 +1127,9 @@ fn upgrade_version_checks_should_work() {
|
||||
let new_code = vec![1, 2, 3, 4];
|
||||
let new_code_hash = H256(sp_core::blake2_256(&new_code));
|
||||
|
||||
let _authorize =
|
||||
ParachainSystem::authorize_upgrade(RawOrigin::Root.into(), new_code_hash, true);
|
||||
#[allow(deprecated)]
|
||||
let _authorize = ParachainSystem::authorize_upgrade(RawOrigin::Root.into(), new_code_hash, true);
|
||||
#[allow(deprecated)]
|
||||
let res = ParachainSystem::enact_authorized_upgrade(RawOrigin::None.into(), new_code);
|
||||
|
||||
assert_eq!(expected.map_err(DispatchErrorWithPostInfo::from), res);
|
||||
|
||||
@@ -152,4 +152,31 @@ impl<T: frame_system::Config> frame_system::WeightInfo for WeightInfo<T> {
|
||||
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into())))
|
||||
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
fn authorize_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 33_027_000 picoseconds.
|
||||
Weight::from_parts(33_027_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Digest` (r:1 w:1)
|
||||
/// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
fn apply_authorized_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `22`
|
||||
// Estimated: `1518`
|
||||
// Minimum execution time: 118_101_992_000 picoseconds.
|
||||
Weight::from_parts(118_101_992_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 1518))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,4 +151,31 @@ impl<T: frame_system::Config> frame_system::WeightInfo for WeightInfo<T> {
|
||||
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into())))
|
||||
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
fn authorize_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 33_027_000 picoseconds.
|
||||
Weight::from_parts(33_027_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Digest` (r:1 w:1)
|
||||
/// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
fn apply_authorized_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `22`
|
||||
// Estimated: `1518`
|
||||
// Minimum execution time: 118_101_992_000 picoseconds.
|
||||
Weight::from_parts(118_101_992_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 1518))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,4 +151,31 @@ impl<T: frame_system::Config> frame_system::WeightInfo for WeightInfo<T> {
|
||||
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into())))
|
||||
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
fn authorize_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 33_027_000 picoseconds.
|
||||
Weight::from_parts(33_027_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Digest` (r:1 w:1)
|
||||
/// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
fn apply_authorized_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `22`
|
||||
// Estimated: `1518`
|
||||
// Minimum execution time: 118_101_992_000 picoseconds.
|
||||
Weight::from_parts(118_101_992_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 1518))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
}
|
||||
|
||||
+27
@@ -152,4 +152,31 @@ impl<T: frame_system::Config> frame_system::WeightInfo for WeightInfo<T> {
|
||||
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into())))
|
||||
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
fn authorize_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 33_027_000 picoseconds.
|
||||
Weight::from_parts(33_027_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Digest` (r:1 w:1)
|
||||
/// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
fn apply_authorized_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `22`
|
||||
// Estimated: `1518`
|
||||
// Minimum execution time: 118_101_992_000 picoseconds.
|
||||
Weight::from_parts(118_101_992_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 1518))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
}
|
||||
|
||||
+27
@@ -151,4 +151,31 @@ impl<T: frame_system::Config> frame_system::WeightInfo for WeightInfo<T> {
|
||||
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into())))
|
||||
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
fn authorize_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 33_027_000 picoseconds.
|
||||
Weight::from_parts(33_027_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Digest` (r:1 w:1)
|
||||
/// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
fn apply_authorized_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `22`
|
||||
// Estimated: `1518`
|
||||
// Minimum execution time: 118_101_992_000 picoseconds.
|
||||
Weight::from_parts(118_101_992_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 1518))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,4 +150,31 @@ impl<T: frame_system::Config> frame_system::WeightInfo for WeightInfo<T> {
|
||||
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into())))
|
||||
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
fn authorize_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 33_027_000 picoseconds.
|
||||
Weight::from_parts(33_027_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Digest` (r:1 w:1)
|
||||
/// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
fn apply_authorized_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `22`
|
||||
// Estimated: `1518`
|
||||
// Minimum execution time: 118_101_992_000 picoseconds.
|
||||
Weight::from_parts(118_101_992_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 1518))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,4 +141,31 @@ impl<T: frame_system::Config> frame_system::WeightInfo for WeightInfo<T> {
|
||||
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into())))
|
||||
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
fn authorize_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 33_027_000 picoseconds.
|
||||
Weight::from_parts(33_027_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Digest` (r:1 w:1)
|
||||
/// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
fn apply_authorized_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `22`
|
||||
// Estimated: `1518`
|
||||
// Minimum execution time: 118_101_992_000 picoseconds.
|
||||
Weight::from_parts(118_101_992_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 1518))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,4 +144,31 @@ impl<T: frame_system::Config> frame_system::WeightInfo for WeightInfo<T> {
|
||||
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into())))
|
||||
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
fn authorize_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 33_027_000 picoseconds.
|
||||
Weight::from_parts(33_027_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Digest` (r:1 w:1)
|
||||
/// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
fn apply_authorized_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `22`
|
||||
// Estimated: `1518`
|
||||
// Minimum execution time: 118_101_992_000 picoseconds.
|
||||
Weight::from_parts(118_101_992_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 1518))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
|
||||
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
|
||||
|
||||
title: "Add Authorize Upgrade Pattern to Frame System"
|
||||
|
||||
doc:
|
||||
- audience: Runtime User
|
||||
description: |
|
||||
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`.
|
||||
- Left calls in `parachain-system` and marked as deprecated to prevent breaking the API. They
|
||||
just call into the `frame-system` functions.
|
||||
- Deprecated calls will be removed no earlier than June 2024.
|
||||
- Updated `frame-system` benchmarks to v2 syntax.
|
||||
|
||||
crates: [ ]
|
||||
@@ -94,6 +94,8 @@ fn module_error_outer_enum_expand_explicit() {
|
||||
frame_system::Error::InvalidTask => (),
|
||||
#[cfg(feature = "experimental")]
|
||||
frame_system::Error::FailedTask => (),
|
||||
frame_system::Error::NothingAuthorized => (),
|
||||
frame_system::Error::Unauthorized => (),
|
||||
frame_system::Error::__Ignore(_, _) => (),
|
||||
},
|
||||
|
||||
|
||||
@@ -94,6 +94,8 @@ fn module_error_outer_enum_expand_implicit() {
|
||||
frame_system::Error::InvalidTask => (),
|
||||
#[cfg(feature = "experimental")]
|
||||
frame_system::Error::FailedTask => (),
|
||||
frame_system::Error::NothingAuthorized => (),
|
||||
frame_system::Error::Unauthorized => (),
|
||||
frame_system::Error::__Ignore(_, _) => (),
|
||||
},
|
||||
|
||||
|
||||
@@ -21,10 +21,7 @@
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
|
||||
use codec::Encode;
|
||||
use frame_benchmarking::{
|
||||
v1::{benchmarks, whitelisted_caller},
|
||||
BenchmarkError,
|
||||
};
|
||||
use frame_benchmarking::{impl_benchmark_test_suite, v2::*};
|
||||
use frame_support::{dispatch::DispatchClass, storage, traits::Get};
|
||||
use frame_system::{Call, Pallet as System, RawOrigin};
|
||||
use sp_core::storage::well_known_keys;
|
||||
@@ -55,69 +52,104 @@ pub trait Config: frame_system::Config {
|
||||
}
|
||||
}
|
||||
|
||||
benchmarks! {
|
||||
remark {
|
||||
let b in 0 .. *T::BlockLength::get().max.get(DispatchClass::Normal) as u32;
|
||||
#[benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn remark(
|
||||
b: Linear<0, { *T::BlockLength::get().max.get(DispatchClass::Normal) as u32 }>,
|
||||
) -> Result<(), BenchmarkError> {
|
||||
let remark_message = vec![1; b as usize];
|
||||
let caller = whitelisted_caller();
|
||||
}: _(RawOrigin::Signed(caller), remark_message)
|
||||
|
||||
remark_with_event {
|
||||
let b in 0 .. *T::BlockLength::get().max.get(DispatchClass::Normal) as u32;
|
||||
let remark_message = vec![1; b as usize];
|
||||
let caller = whitelisted_caller();
|
||||
}: _(RawOrigin::Signed(caller), remark_message)
|
||||
#[extrinsic_call]
|
||||
remark(RawOrigin::Signed(caller), remark_message);
|
||||
|
||||
set_heap_pages {
|
||||
}: _(RawOrigin::Root, Default::default())
|
||||
|
||||
set_code {
|
||||
let runtime_blob = T::prepare_set_code_data();
|
||||
T::setup_set_code_requirements(&runtime_blob)?;
|
||||
}: _(RawOrigin::Root, runtime_blob)
|
||||
verify {
|
||||
T::verify_set_code()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[extra]
|
||||
set_code_without_checks {
|
||||
#[benchmark]
|
||||
fn remark_with_event(
|
||||
b: Linear<0, { *T::BlockLength::get().max.get(DispatchClass::Normal) as u32 }>,
|
||||
) -> Result<(), BenchmarkError> {
|
||||
let remark_message = vec![1; b as usize];
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
let hash = T::Hashing::hash(&remark_message[..]);
|
||||
|
||||
#[extrinsic_call]
|
||||
remark_with_event(RawOrigin::Signed(caller.clone()), remark_message);
|
||||
|
||||
System::<T>::assert_last_event(
|
||||
frame_system::Event::<T>::Remarked { sender: caller, hash }.into(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn set_heap_pages() -> Result<(), BenchmarkError> {
|
||||
#[extrinsic_call]
|
||||
set_heap_pages(RawOrigin::Root, Default::default());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn set_code() -> Result<(), BenchmarkError> {
|
||||
let runtime_blob = T::prepare_set_code_data();
|
||||
T::setup_set_code_requirements(&runtime_blob)?;
|
||||
|
||||
#[extrinsic_call]
|
||||
set_code(RawOrigin::Root, runtime_blob);
|
||||
|
||||
T::verify_set_code();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark(extra)]
|
||||
fn set_code_without_checks() -> Result<(), BenchmarkError> {
|
||||
// Assume Wasm ~4MB
|
||||
let code = vec![1; 4_000_000 as usize];
|
||||
T::setup_set_code_requirements(&code)?;
|
||||
}: _(RawOrigin::Root, code)
|
||||
verify {
|
||||
let current_code = storage::unhashed::get_raw(well_known_keys::CODE).ok_or("Code not stored.")?;
|
||||
|
||||
#[block]
|
||||
{
|
||||
System::<T>::set_code_without_checks(RawOrigin::Root.into(), code)?;
|
||||
}
|
||||
|
||||
let current_code =
|
||||
storage::unhashed::get_raw(well_known_keys::CODE).ok_or("Code not stored.")?;
|
||||
assert_eq!(current_code.len(), 4_000_000 as usize);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[skip_meta]
|
||||
set_storage {
|
||||
let i in 0 .. 1000;
|
||||
|
||||
#[benchmark(skip_meta)]
|
||||
fn set_storage(i: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
|
||||
// Set up i items to add
|
||||
let mut items = Vec::new();
|
||||
for j in 0 .. i {
|
||||
for j in 0..i {
|
||||
let hash = (i, j).using_encoded(T::Hashing::hash).as_ref().to_vec();
|
||||
items.push((hash.clone(), hash.clone()));
|
||||
}
|
||||
|
||||
let items_to_verify = items.clone();
|
||||
}: _(RawOrigin::Root, items)
|
||||
verify {
|
||||
|
||||
#[extrinsic_call]
|
||||
set_storage(RawOrigin::Root, items);
|
||||
|
||||
// Verify that they're actually in the storage.
|
||||
for (item, _) in items_to_verify {
|
||||
let value = storage::unhashed::get_raw(&item).ok_or("No value stored")?;
|
||||
assert_eq!(value, *item);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[skip_meta]
|
||||
kill_storage {
|
||||
let i in 0 .. 1000;
|
||||
|
||||
#[benchmark(skip_meta)]
|
||||
fn kill_storage(i: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
|
||||
// Add i items to storage
|
||||
let mut items = Vec::with_capacity(i as usize);
|
||||
for j in 0 .. i {
|
||||
for j in 0..i {
|
||||
let hash = (i, j).using_encoded(T::Hashing::hash).as_ref().to_vec();
|
||||
storage::unhashed::put_raw(&hash, &hash);
|
||||
items.push(hash);
|
||||
@@ -130,22 +162,23 @@ benchmarks! {
|
||||
}
|
||||
|
||||
let items_to_verify = items.clone();
|
||||
}: _(RawOrigin::Root, items)
|
||||
verify {
|
||||
|
||||
#[extrinsic_call]
|
||||
kill_storage(RawOrigin::Root, items);
|
||||
|
||||
// Verify that they're not in the storage anymore.
|
||||
for item in items_to_verify {
|
||||
assert!(storage::unhashed::get_raw(&item).is_none());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[skip_meta]
|
||||
kill_prefix {
|
||||
let p in 0 .. 1000;
|
||||
|
||||
#[benchmark(skip_meta)]
|
||||
fn kill_prefix(p: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
|
||||
let prefix = p.using_encoded(T::Hashing::hash).as_ref().to_vec();
|
||||
let mut items = Vec::with_capacity(p as usize);
|
||||
// add p items that share a prefix
|
||||
for i in 0 .. p {
|
||||
for i in 0..p {
|
||||
let hash = (p, i).using_encoded(T::Hashing::hash).as_ref().to_vec();
|
||||
let key = [&prefix[..], &hash[..]].concat();
|
||||
storage::unhashed::put_raw(&key, &key);
|
||||
@@ -157,12 +190,45 @@ benchmarks! {
|
||||
let value = storage::unhashed::get_raw(item).ok_or("No value stored")?;
|
||||
assert_eq!(value, *item);
|
||||
}
|
||||
}: _(RawOrigin::Root, prefix, p)
|
||||
verify {
|
||||
|
||||
#[extrinsic_call]
|
||||
kill_prefix(RawOrigin::Root, prefix, p);
|
||||
|
||||
// Verify that they're not in the storage anymore.
|
||||
for item in items {
|
||||
assert!(storage::unhashed::get_raw(&item).is_none());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn authorize_upgrade() -> Result<(), BenchmarkError> {
|
||||
let runtime_blob = T::prepare_set_code_data();
|
||||
T::setup_set_code_requirements(&runtime_blob)?;
|
||||
let hash = T::Hashing::hash(&runtime_blob);
|
||||
|
||||
#[extrinsic_call]
|
||||
authorize_upgrade(RawOrigin::Root, hash);
|
||||
|
||||
assert!(System::<T>::authorized_upgrade().is_some());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn apply_authorized_upgrade() -> Result<(), BenchmarkError> {
|
||||
let runtime_blob = T::prepare_set_code_data();
|
||||
T::setup_set_code_requirements(&runtime_blob)?;
|
||||
let hash = T::Hashing::hash(&runtime_blob);
|
||||
// Will be heavier when it needs to do verification (i.e. don't use `...without_checks`).
|
||||
System::<T>::authorize_upgrade(RawOrigin::Root.into(), hash)?;
|
||||
|
||||
#[extrinsic_call]
|
||||
apply_authorized_upgrade(RawOrigin::Root, runtime_blob);
|
||||
|
||||
// Can't check for `CodeUpdated` in parachain upgrades. Just check that the authorization is
|
||||
// gone.
|
||||
assert!(System::<T>::authorized_upgrade().is_none());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -675,6 +675,46 @@ fn set_code_with_real_wasm_blob() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_code_via_authorization_works() {
|
||||
let executor = substrate_test_runtime_client::new_native_or_wasm_executor();
|
||||
let mut ext = new_test_ext();
|
||||
ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor));
|
||||
ext.execute_with(|| {
|
||||
System::set_block_number(1);
|
||||
assert!(System::authorized_upgrade().is_none());
|
||||
|
||||
let runtime = substrate_test_runtime_client::runtime::wasm_binary_unwrap().to_vec();
|
||||
let hash = <mock::Test as pallet::Config>::Hashing::hash(&runtime);
|
||||
|
||||
// Can't apply before authorization
|
||||
assert_noop!(
|
||||
System::apply_authorized_upgrade(RawOrigin::None.into(), runtime.clone()),
|
||||
Error::<Test>::NothingAuthorized,
|
||||
);
|
||||
|
||||
// Can authorize
|
||||
assert_ok!(System::authorize_upgrade(RawOrigin::Root.into(), hash));
|
||||
System::assert_has_event(
|
||||
SysEvent::UpgradeAuthorized { code_hash: hash, check_version: true }.into(),
|
||||
);
|
||||
assert!(System::authorized_upgrade().is_some());
|
||||
|
||||
// Can't be sneaky
|
||||
let mut bad_runtime = substrate_test_runtime_client::runtime::wasm_binary_unwrap().to_vec();
|
||||
bad_runtime.extend(b"sneaky");
|
||||
assert_noop!(
|
||||
System::apply_authorized_upgrade(RawOrigin::None.into(), bad_runtime),
|
||||
Error::<Test>::Unauthorized,
|
||||
);
|
||||
|
||||
// Can apply correct runtime
|
||||
assert_ok!(System::apply_authorized_upgrade(RawOrigin::None.into(), runtime));
|
||||
System::assert_has_event(SysEvent::CodeUpdated.into());
|
||||
assert!(System::authorized_upgrade().is_none());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_upgraded_with_set_storage() {
|
||||
let executor = substrate_test_runtime_client::new_native_or_wasm_executor();
|
||||
|
||||
Generated
+56
@@ -57,6 +57,8 @@ pub trait WeightInfo {
|
||||
fn set_storage(i: u32, ) -> Weight;
|
||||
fn kill_storage(i: u32, ) -> Weight;
|
||||
fn kill_prefix(p: u32, ) -> Weight;
|
||||
fn authorize_upgrade() -> Weight;
|
||||
fn apply_authorized_upgrade() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for frame_system using the Substrate node and recommended hardware.
|
||||
@@ -149,6 +151,33 @@ impl<T: crate::Config> WeightInfo for SubstrateWeight<T> {
|
||||
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into())))
|
||||
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
fn authorize_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 33_027_000 picoseconds.
|
||||
Weight::from_parts(33_027_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Digest` (r:1 w:1)
|
||||
/// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
fn apply_authorized_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `22`
|
||||
// Estimated: `1518`
|
||||
// Minimum execution time: 118_101_992_000 picoseconds.
|
||||
Weight::from_parts(118_101_992_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 1518))
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
@@ -240,4 +269,31 @@ impl WeightInfo for () {
|
||||
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into())))
|
||||
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
fn authorize_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 33_027_000 picoseconds.
|
||||
Weight::from_parts(33_027_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 0))
|
||||
.saturating_add(RocksDbWeight::get().writes(1))
|
||||
}
|
||||
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
|
||||
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Digest` (r:1 w:1)
|
||||
/// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
|
||||
fn apply_authorized_upgrade() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `22`
|
||||
// Estimated: `1518`
|
||||
// Minimum execution time: 118_101_992_000 picoseconds.
|
||||
Weight::from_parts(118_101_992_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 1518))
|
||||
.saturating_add(RocksDbWeight::get().reads(2))
|
||||
.saturating_add(RocksDbWeight::get().writes(3))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user