Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

paras: fix upgrade restriction signal #4603

Merged
merged 1 commit into from
Dec 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions runtime/parachains/src/dmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ mod tests {
pub(crate) fn run_to_block(to: BlockNumber, new_session: Option<Vec<BlockNumber>>) {
while System::block_number() < to {
let b = System::block_number();
Paras::initializer_finalize();
Paras::initializer_finalize(b);
Dmp::initializer_finalize();
if new_session.as_ref().map_or(false, |v| v.contains(&(b + 1))) {
Dmp::initializer_on_new_session(&Default::default(), &Vec::new());
Expand All @@ -248,7 +248,7 @@ mod tests {
System::on_initialize(b + 1);
System::set_block_number(b + 1);

Paras::initializer_finalize();
Paras::initializer_finalize(b + 1);
Dmp::initializer_initialize(b + 1);
}
}
Expand Down
2 changes: 1 addition & 1 deletion runtime/parachains/src/hrmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1281,7 +1281,7 @@ mod tests {

// NOTE: this is in reverse initialization order.
Hrmp::initializer_finalize();
Paras::initializer_finalize();
Paras::initializer_finalize(b);
ParasShared::initializer_finalize();

if new_session.as_ref().map_or(false, |v| v.contains(&(b + 1))) {
Expand Down
2 changes: 1 addition & 1 deletion runtime/parachains/src/inclusion/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ pub(crate) fn run_to_block(
let b = System::block_number();

ParaInclusion::initializer_finalize();
Paras::initializer_finalize();
Paras::initializer_finalize(b);
ParasShared::initializer_finalize();

if let Some(notification) = new_session(b + 1) {
Expand Down
4 changes: 2 additions & 2 deletions runtime/parachains/src/initializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ pub mod pallet {
total_weight
}

fn on_finalize(_: T::BlockNumber) {
fn on_finalize(now: T::BlockNumber) {
// reverse initialization order.
hrmp::Pallet::<T>::initializer_finalize();
ump::Pallet::<T>::initializer_finalize();
Expand All @@ -177,7 +177,7 @@ pub mod pallet {
session_info::Pallet::<T>::initializer_finalize();
inclusion::Pallet::<T>::initializer_finalize();
scheduler::Pallet::<T>::initializer_finalize();
paras::Pallet::<T>::initializer_finalize();
paras::Pallet::<T>::initializer_finalize(now);
shared::Pallet::<T>::initializer_finalize();
configuration::Pallet::<T>::initializer_finalize();

Expand Down
119 changes: 108 additions & 11 deletions runtime/parachains/src/paras.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,9 @@ impl<T: Config> Pallet<T> {
}

/// Called by the initializer to finalize the configuration pallet.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not your fault, but the comment is c&p?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, but it does look correct, or?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the configuration pallet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah gotcha xD will fix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub(crate) fn initializer_finalize() {}
pub(crate) fn initializer_finalize(now: T::BlockNumber) {
Self::process_scheduled_upgrade_cooldowns(now);
}

/// Called by the initializer to note that a new session has started.
///
Expand Down Expand Up @@ -1232,10 +1234,13 @@ impl<T: Config> Pallet<T> {
}

/// Process the timers related to upgrades. Specifically, the upgrade go ahead signals toggle
/// and the upgrade cooldown restrictions.
///
/// Takes the current block number and returns the weight consumed.
/// and the upgrade cooldown restrictions. However, this function does not actually unset
/// the upgrade restriction, that will happen in the `initializer_finalize` function. However,
/// this function does count the number of cooldown timers expired so that we can reserve weight
/// for the `initializer_finalize` function.
fn process_scheduled_upgrade_changes(now: T::BlockNumber) -> Weight {
// account weight for `UpcomingUpgrades::mutate`.
let mut weight = T::DbWeight::get().reads_writes(1, 1);
let upgrades_signaled = <Self as Store>::UpcomingUpgrades::mutate(
|upcoming_upgrades: &mut Vec<(ParaId, T::BlockNumber)>| {
let num = upcoming_upgrades.iter().take_while(|&(_, at)| at <= &now).count();
Expand All @@ -1245,17 +1250,35 @@ impl<T: Config> Pallet<T> {
num
},
);
let cooldowns_expired = <Self as Store>::UpgradeCooldowns::mutate(
weight += T::DbWeight::get().writes(upgrades_signaled as u64);

// account weight for `UpgradeCooldowns::get`.
weight += T::DbWeight::get().reads(1);
let cooldowns_expired = <Self as Store>::UpgradeCooldowns::get()
.iter()
.take_while(|&(_, at)| at <= &now)
.count();

// reserve weight for `initializer_finalize`:
// - 1 read and 1 write for `UpgradeCooldowns::mutate`.
// - 1 write per expired cooldown.
weight += T::DbWeight::get().reads_writes(1, 1);
weight += T::DbWeight::get().reads(cooldowns_expired as u64);

weight
}

/// Actually perform unsetting the expired upgrade restrictions.
///
/// See `process_scheduled_upgrade_changes` for more details.
fn process_scheduled_upgrade_cooldowns(now: T::BlockNumber) {
<Self as Store>::UpgradeCooldowns::mutate(
|upgrade_cooldowns: &mut Vec<(ParaId, T::BlockNumber)>| {
let num = upgrade_cooldowns.iter().take_while(|&(_, at)| at <= &now).count();
for (para, _) in upgrade_cooldowns.drain(..num) {
for &(para, _) in upgrade_cooldowns.iter().take_while(|&(_, at)| at <= &now) {
<Self as Store>::UpgradeRestrictionSignal::remove(&para);
}
num
},
);

T::DbWeight::get().reads_writes(2, upgrades_signaled as u64 + cooldowns_expired as u64)
}

/// Goes over all PVF votes in progress, reinitializes ballots, increments ages and prunes the
Expand Down Expand Up @@ -1985,7 +2008,7 @@ mod tests {

while System::block_number() < to {
let b = System::block_number();
Paras::initializer_finalize();
Paras::initializer_finalize(b);
ParasShared::initializer_finalize();
if new_session.as_ref().map_or(false, |v| v.contains(&(b + 1))) {
let mut session_change_notification = SessionChangeNotification::default();
Expand Down Expand Up @@ -2510,6 +2533,7 @@ mod tests {
// We expect that if an upgrade is signalled while there is already one pending we just
// ignore it. Note that this is only true from perspective of this module.
run_to_block(2, None);
assert!(!Paras::can_upgrade_validation_code(para_id));
Paras::schedule_code_upgrade(para_id, newer_code.clone(), 2, &Configuration::config());
assert_eq!(
<Paras as Store>::FutureCodeUpgrades::get(&para_id),
Expand All @@ -2520,6 +2544,79 @@ mod tests {
});
}

#[test]
fn upgrade_restriction_elapsed_doesnt_mean_can_upgrade() {
// Situation: parachain scheduled upgrade but it doesn't produce any candidate after
// `expected_at`. When `validation_upgrade_cooldown` elapsed the parachain produces a
// candidate that tries to upgrade the code.
//
// In the current code this is not allowed: the upgrade should be consumed first. This is
// rather an artifact of the current implementation and not necessarily something we want
// to keep in the future.
//
// This test exists that this is not accidentially changed.

let code_retention_period = 10;
let validation_upgrade_delay = 7;
let validation_upgrade_cooldown = 30;

let paras = vec![(
0u32.into(),
ParaGenesisArgs {
parachain: true,
genesis_head: dummy_head_data(),
validation_code: vec![1, 2, 3].into(),
},
)];

let genesis_config = MockGenesisConfig {
paras: GenesisConfig { paras, ..Default::default() },
configuration: crate::configuration::GenesisConfig {
config: HostConfiguration {
code_retention_period,
validation_upgrade_delay,
validation_upgrade_cooldown,
pvf_checking_enabled: false,
..Default::default()
},
..Default::default()
},
..Default::default()
};

new_test_ext(genesis_config).execute_with(|| {
let para_id = 0u32.into();
let new_code = ValidationCode(vec![4, 5, 6]);
let newer_code = ValidationCode(vec![4, 5, 6, 7]);

run_to_block(1, None);
Paras::schedule_code_upgrade(para_id, new_code.clone(), 0, &Configuration::config());
Paras::note_new_head(para_id, dummy_head_data(), 0);
assert_eq!(
<Paras as Store>::UpgradeRestrictionSignal::get(&para_id),
Some(UpgradeRestriction::Present),
);
assert_eq!(
<Paras as Store>::FutureCodeUpgrades::get(&para_id),
Some(0 + validation_upgrade_delay)
);
assert!(!Paras::can_upgrade_validation_code(para_id));

run_to_block(31, None);
assert!(<Paras as Store>::UpgradeRestrictionSignal::get(&para_id).is_none());

// Note the para still cannot upgrade the validation code.
assert!(!Paras::can_upgrade_validation_code(para_id));

// And scheduling another upgrade does not do anything. `expected_at` is still the same.
Paras::schedule_code_upgrade(para_id, newer_code.clone(), 30, &Configuration::config());
assert_eq!(
<Paras as Store>::FutureCodeUpgrades::get(&para_id),
Some(0 + validation_upgrade_delay)
);
});
}

#[test]
fn full_parachain_cleanup_storage() {
let code_retention_period = 20;
Expand Down
4 changes: 2 additions & 2 deletions runtime/parachains/src/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,7 @@ mod tests {
let b = System::block_number();

Scheduler::initializer_finalize();
Paras::initializer_finalize();
Paras::initializer_finalize(b);

if let Some(notification) = new_session(b + 1) {
let mut notification_with_session_index = notification;
Expand Down Expand Up @@ -831,7 +831,7 @@ mod tests {
run_to_block(to, &new_session);

Scheduler::initializer_finalize();
Paras::initializer_finalize();
Paras::initializer_finalize(to);

if let Some(notification) = new_session(to + 1) {
Paras::initializer_on_new_session(&notification);
Expand Down