Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

frame-system: Add last_runtime_upgrade_spec_version #2351

Merged
merged 2 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 32 additions & 26 deletions substrate/frame/executive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,12 @@ where
let mut weight = Weight::zero();
if Self::runtime_upgraded() {
weight = weight.saturating_add(Self::execute_on_runtime_upgrade());

frame_system::LastRuntimeUpgrade::<System>::put(
frame_system::LastRuntimeUpgradeInfo::from(
<System::Version as frame_support::traits::Get<_>>::get(),
),
);
liamaharon marked this conversation as resolved.
Show resolved Hide resolved
}
<frame_system::Pallet<System>>::initialize(block_number, parent_hash, digest);
weight = weight.saturating_add(<AllPalletsWithSystem as OnInitialize<
Expand All @@ -503,19 +509,14 @@ where
frame_system::Pallet::<System>::note_finished_initialize();
}

/// Returns if the runtime was upgraded since the last time this function was called.
/// Returns if the runtime was upgraded.
///
/// based on [`frame_system::LastRuntimeUpgrade`]
bkchr marked this conversation as resolved.
Show resolved Hide resolved
fn runtime_upgraded() -> bool {
let last = frame_system::LastRuntimeUpgrade::<System>::get();
let current = <System::Version as frame_support::traits::Get<_>>::get();

if last.map(|v| v.was_upgraded(&current)).unwrap_or(true) {
frame_system::LastRuntimeUpgrade::<System>::put(
frame_system::LastRuntimeUpgradeInfo::from(current),
);
true
} else {
false
}
last.map(|v| v.was_upgraded(&current)).unwrap_or(true)
}

fn initial_checks(block: &Block) {
Expand Down Expand Up @@ -755,7 +756,7 @@ mod tests {
traits::{fungible, ConstU32, ConstU64, ConstU8, Currency},
weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight, WeightToFee},
};
use frame_system::{ChainContext, LastRuntimeUpgradeInfo};
use frame_system::{ChainContext, LastRuntimeUpgrade, LastRuntimeUpgradeInfo};
use pallet_balances::Call as BalancesCall;
use pallet_transaction_payment::CurrencyAdapter;

Expand Down Expand Up @@ -994,6 +995,9 @@ mod tests {
sp_io::storage::set(TEST_KEY, "custom_upgrade".as_bytes());
sp_io::storage::set(CUSTOM_ON_RUNTIME_KEY, &true.encode());
System::deposit_event(frame_system::Event::CodeUpdated);

assert_eq!(0, System::last_runtime_upgrade_spec_version());

Weight::from_parts(100, 0)
}
}
Expand Down Expand Up @@ -1356,17 +1360,13 @@ mod tests {
new_test_ext(1).execute_with(|| {
RuntimeVersionTestValues::mutate(|v| *v = Default::default());
// It should be added at genesis
assert!(frame_system::LastRuntimeUpgrade::<Runtime>::exists());
assert!(LastRuntimeUpgrade::<Runtime>::exists());
assert!(!Executive::runtime_upgraded());

RuntimeVersionTestValues::mutate(|v| {
*v = sp_version::RuntimeVersion { spec_version: 1, ..Default::default() }
});
assert!(Executive::runtime_upgraded());
assert_eq!(
Some(LastRuntimeUpgradeInfo { spec_version: 1.into(), spec_name: "".into() }),
frame_system::LastRuntimeUpgrade::<Runtime>::get(),
);

RuntimeVersionTestValues::mutate(|v| {
*v = sp_version::RuntimeVersion {
Expand All @@ -1376,27 +1376,18 @@ mod tests {
}
});
assert!(Executive::runtime_upgraded());
assert_eq!(
Some(LastRuntimeUpgradeInfo { spec_version: 1.into(), spec_name: "test".into() }),
frame_system::LastRuntimeUpgrade::<Runtime>::get(),
);

RuntimeVersionTestValues::mutate(|v| {
*v = sp_version::RuntimeVersion {
spec_version: 1,
spec_name: "test".into(),
spec_version: 0,
impl_version: 2,
..Default::default()
}
});
assert!(!Executive::runtime_upgraded());

frame_system::LastRuntimeUpgrade::<Runtime>::take();
LastRuntimeUpgrade::<Runtime>::take();
assert!(Executive::runtime_upgraded());
assert_eq!(
Some(LastRuntimeUpgradeInfo { spec_version: 1.into(), spec_name: "test".into() }),
frame_system::LastRuntimeUpgrade::<Runtime>::get(),
);
})
}

Expand Down Expand Up @@ -1444,6 +1435,10 @@ mod tests {

assert_eq!(&sp_io::storage::get(TEST_KEY).unwrap()[..], *b"module");
assert_eq!(sp_io::storage::get(CUSTOM_ON_RUNTIME_KEY).unwrap(), true.encode());
assert_eq!(
Some(RuntimeVersionTestValues::get().into()),
LastRuntimeUpgrade::<Runtime>::get(),
)
});
}

Expand Down Expand Up @@ -1519,6 +1514,11 @@ mod tests {

#[test]
fn all_weights_are_recorded_correctly() {
// Reset to get the correct new genesis below.
RuntimeVersionTestValues::mutate(|v| {
*v = sp_version::RuntimeVersion { spec_version: 0, ..Default::default() }
});
bkchr marked this conversation as resolved.
Show resolved Hide resolved

new_test_ext(1).execute_with(|| {
// Make sure `on_runtime_upgrade` is called for maximum complexity
RuntimeVersionTestValues::mutate(|v| {
Expand All @@ -1535,6 +1535,12 @@ mod tests {
Digest::default(),
));

// Reset the last runtime upgrade info, to make the second call to `on_runtime_upgrade`
// succeed.
LastRuntimeUpgrade::<Runtime>::set(Some(
sp_version::RuntimeVersion { spec_version: 0, ..Default::default() }.into(),
));
bkchr marked this conversation as resolved.
Show resolved Hide resolved

// All weights that show up in the `initialize_block_impl`
let custom_runtime_upgrade_weight = CustomOnRuntimeUpgrade::on_runtime_upgrade();
let runtime_upgrade_weight =
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/system/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ sp-runtime = { path = "../../primitives/runtime", default-features = false, feat
sp-std = { path = "../../primitives/std", default-features = false}
sp-version = { path = "../../primitives/version", default-features = false, features = ["serde"] }
sp-weights = { path = "../../primitives/weights", default-features = false, features = ["serde"] }
docify = "0.2.0"

[dev-dependencies]
criterion = "0.4.0"
Expand Down
19 changes: 19 additions & 0 deletions substrate/frame/system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,25 @@ pub enum DecRefStatus {
}

impl<T: Config> Pallet<T> {
/// Returns the `spec_version` of the last runtime upgrade.
///
/// This function is useful for writing guarded runtime migrations in the runtime. A runtime
/// migration can use the `spec_version` to ensure that it isn't applied twice. This works
/// similar as the storage version for pallets.
///
/// This functions returns the `spec_version` of the last runtime upgrade while executing the
/// runtime migrations
/// [`on_runtime_upgrade`](frame_support::traits::OnRuntimeUpgrade::on_runtime_upgrade)
/// function. After all migrations are executed, this will return the `spec_version` of the
/// current runtime until there is another runtime upgrade.
///
/// Example:
#[doc = docify::embed!("src/tests.rs", last_runtime_upgrade_spec_version_usage)]
pub fn last_runtime_upgrade_spec_version() -> u32 {
LastRuntimeUpgrade::<T>::get().map_or(0, |l| l.spec_version.0)
}

/// Returns true if the given account exists.
pub fn account_exists(who: &T::AccountId) -> bool {
Account::<T>::contains_key(who)
}
Expand Down
26 changes: 25 additions & 1 deletion substrate/frame/system/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::*;
use frame_support::{
assert_noop, assert_ok,
dispatch::{Pays, PostDispatchInfo, WithPostDispatchInfo},
traits::WhitelistedStorageKeys,
traits::{OnRuntimeUpgrade, WhitelistedStorageKeys},
};
use std::collections::BTreeSet;

Expand Down Expand Up @@ -773,3 +773,27 @@ pub fn from_actual_ref_time(ref_time: Option<u64>) -> PostDispatchInfo {
pub fn from_post_weight_info(ref_time: Option<u64>, pays_fee: Pays) -> PostDispatchInfo {
PostDispatchInfo { actual_weight: ref_time.map(|t| Weight::from_all(t)), pays_fee }
}

#[docify::export]
#[test]
fn last_runtime_upgrade_spec_version_usage() {
struct Migration;

impl OnRuntimeUpgrade for Migration {
fn on_runtime_upgrade() -> Weight {
// Ensure to compare the spec version against some static version to prevent applying
// the same migration multiple times.
//
// `1337` here is the spec version of the runtime running on chain. If there is maybe
// a runtime upgrade in the pipeline of being applied, you should use the spec version
// of this upgrade.
if System::last_runtime_upgrade_spec_version() > 1337 {
return Weight::zero();
}

// Do the migration.

bkchr marked this conversation as resolved.
Show resolved Hide resolved
Weight::zero()
}
}
}