Skip to content
16 changes: 14 additions & 2 deletions polkadot/runtime/common/src/paras_registrar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use frame_system::{self, ensure_root, ensure_signed};
use primitives::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID, MIN_CODE_SIZE};
use runtime_parachains::{
configuration, ensure_parachain,
paras::{self, ParaGenesisArgs, SetGoAhead},
paras::{self, EnactUpgradeDirectly, ParaGenesisArgs},
Origin, ParaLifecycle,
};
use sp_std::{prelude::*, result};
Expand Down Expand Up @@ -408,6 +408,14 @@ pub mod pallet {

/// Schedule a parachain upgrade.
///
/// This will kick off a check of `new_code` by all validators. After the majority of the
/// validators have reported on the validity of the code, the code will either be enacted
/// or the upgrade will be rejected. If the code will be enacted, the current code of the
/// parachain will be overwritten directly. This means that any PoV will be checked by this
/// new code. The parachain will not be aware that the code changes. Thus, this should only
/// be used if the parachain e.g. is broken and requires some manual fixing. Do not use this
/// for upgrading your parachain as this can brick the parachain.
///
/// Can be called by Root, the parachain, or the parachain manager if the parachain is
/// unlocked.
#[pallet::call_index(7)]
Expand All @@ -418,7 +426,11 @@ pub mod pallet {
new_code: ValidationCode,
) -> DispatchResult {
Self::ensure_root_para_or_owner(origin, para)?;
runtime_parachains::schedule_code_upgrade::<T>(para, new_code, SetGoAhead::No)?;
runtime_parachains::schedule_code_upgrade::<T>(
para,
new_code,
EnactUpgradeDirectly::Yes,
)?;
Ok(())
}

Expand Down
4 changes: 2 additions & 2 deletions polkadot/runtime/parachains/src/inclusion/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
use crate::{
configuration::{self, HostConfiguration},
disputes, dmp, hrmp,
paras::{self, SetGoAhead},
paras::{self, EnactUpgradeDirectly},
scheduler::{self, AvailabilityTimeoutStatus},
shared::{self, AllowedRelayParentsTracker},
};
Expand Down Expand Up @@ -887,7 +887,7 @@ impl<T: Config> Pallet<T> {
new_code,
now,
&config,
SetGoAhead::Yes,
EnactUpgradeDirectly::No,
));
}

Expand Down
4 changes: 2 additions & 2 deletions polkadot/runtime/parachains/src/inclusion/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,7 @@ fn candidate_checks() {
vec![9, 8, 7, 6, 5, 4, 3, 2, 1].into(),
expected_at,
&cfg,
SetGoAhead::Yes,
EnactUpgradeDirectly::No,
);
}

Expand Down Expand Up @@ -2241,7 +2241,7 @@ fn para_upgrade_delay_scheduled_from_inclusion() {
let cause = &active_vote_state.causes()[0];
// Upgrade block is the block of inclusion, not candidate's parent.
assert_matches!(cause,
paras::PvfCheckCause::Upgrade { id, included_at, set_go_ahead: SetGoAhead::Yes }
paras::PvfCheckCause::Upgrade { id, included_at, enact_upgrade_directly: EnactUpgradeDirectly::No }
if id == &chain_a && included_at == &7
);
});
Expand Down
4 changes: 2 additions & 2 deletions polkadot/runtime/parachains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ mod mock;
mod ump_tests;

pub use origin::{ensure_parachain, Origin};
pub use paras::{ParaLifecycle, SetGoAhead};
pub use paras::{ParaLifecycle, EnactUpgradeDirectly};
use primitives::{HeadData, Id as ParaId, ValidationCode};
use sp_runtime::{DispatchResult, FixedU128};

Expand Down Expand Up @@ -104,7 +104,7 @@ pub fn schedule_parachain_downgrade<T: paras::Config>(id: ParaId) -> Result<(),
pub fn schedule_code_upgrade<T: paras::Config>(
id: ParaId,
new_code: ValidationCode,
set_go_ahead: SetGoAhead,
set_go_ahead: EnactUpgradeDirectly,
) -> DispatchResult {
paras::Pallet::<T>::schedule_code_upgrade_external(id, new_code, set_go_ahead)
}
Expand Down
2 changes: 1 addition & 1 deletion polkadot/runtime/parachains/src/paras/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ benchmarks! {
ValidationCode(vec![0]),
expired,
&config,
SetGoAhead::Yes,
EnactUpgradeDirectly::No,
);
}: _(RawOrigin::Root, para_id, new_head)
verify {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ where
validation_code,
/* relay_parent_number */ 1u32.into(),
&configuration::Pallet::<T>::config(),
SetGoAhead::Yes,
EnactUpgradeDirectly::No,
);
} else {
let r = Pallet::<T>::schedule_para_initialize(
Expand Down
143 changes: 77 additions & 66 deletions polkadot/runtime/parachains/src/paras/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,16 +386,19 @@ pub(crate) enum PvfCheckCause<BlockNumber> {
///
/// See https://github.com/paritytech/polkadot/issues/4601 for detailed explanation.
included_at: BlockNumber,
/// Whether or not the given para should be sent the `GoAhead` signal.
set_go_ahead: SetGoAhead,
/// Whether or not the upgrade should be enacted directly.
///
/// If set to `Yes` it means that no `GoAheadSignal` will be set and the parachain code
/// will also be overwritten directly.
enact_upgrade_directly: EnactUpgradeDirectly,
},
}

/// Should the `GoAhead` signal be set after a successful check of the new wasm binary?
/// Should the upgrade of the validation code be enacted directly after the checks succeeded?
#[derive(Debug, Copy, Clone, PartialEq, TypeInfo, Decode, Encode)]
pub enum SetGoAhead {
Yes,
pub enum EnactUpgradeDirectly {
No,
Yes,
}

impl<BlockNumber> PvfCheckCause<BlockNumber> {
Expand Down Expand Up @@ -880,21 +883,9 @@ pub mod pallet {
new_code: ValidationCode,
) -> DispatchResult {
ensure_root(origin)?;
let maybe_prior_code_hash = CurrentCodeHash::<T>::get(&para);
let new_code_hash = new_code.hash();
Self::increase_code_ref(&new_code_hash, &new_code);
CurrentCodeHash::<T>::insert(&para, new_code_hash);

let now = frame_system::Pallet::<T>::block_number();
if let Some(prior_code_hash) = maybe_prior_code_hash {
Self::note_past_code(para, now, now, prior_code_hash);
} else {
log::error!(
target: LOG_TARGET,
"Pallet paras storage is inconsistent, prior code not found {:?}",
&para
);
}
Self::set_current_code(para, new_code_hash, frame_system::Pallet::<T>::block_number());
Self::deposit_event(Event::CurrentCodeUpdated(para));
Ok(())
}
Expand Down Expand Up @@ -928,7 +919,7 @@ pub mod pallet {
new_code,
relay_parent_number,
&config,
SetGoAhead::No,
EnactUpgradeDirectly::Yes,
);
Self::deposit_event(Event::CodeUpgradeScheduled(para));
Ok(())
Expand Down Expand Up @@ -1227,7 +1218,7 @@ impl<T: Config> Pallet<T> {
pub(crate) fn schedule_code_upgrade_external(
id: ParaId,
new_code: ValidationCode,
set_go_ahead: SetGoAhead,
enact_upgrade_directly: EnactUpgradeDirectly,
) -> DispatchResult {
// Check that we can schedule an upgrade at all.
ensure!(Self::can_upgrade_validation_code(id), Error::<T>::CannotUpgradeCode);
Expand All @@ -1239,7 +1230,7 @@ impl<T: Config> Pallet<T> {
let current_block = frame_system::Pallet::<T>::block_number();
// Schedule the upgrade with a delay just like if a parachain triggered the upgrade.
let upgrade_block = current_block.saturating_add(config.validation_upgrade_delay);
Self::schedule_code_upgrade(id, new_code, upgrade_block, &config, set_go_ahead);
Self::schedule_code_upgrade(id, new_code, upgrade_block, &config, enact_upgrade_directly);
Self::deposit_event(Event::CodeUpgradeScheduled(id));
Ok(())
}
Expand Down Expand Up @@ -1580,14 +1571,14 @@ impl<T: Config> Pallet<T> {
PvfCheckCause::Onboarding(id) => {
weight += Self::proceed_with_onboarding(*id, sessions_observed);
},
PvfCheckCause::Upgrade { id, included_at, set_go_ahead } => {
PvfCheckCause::Upgrade { id, included_at, enact_upgrade_directly } => {
weight += Self::proceed_with_upgrade(
*id,
code_hash,
now,
*included_at,
cfg,
*set_go_ahead,
*enact_upgrade_directly,
);
},
}
Expand Down Expand Up @@ -1621,43 +1612,45 @@ impl<T: Config> Pallet<T> {
now: BlockNumberFor<T>,
relay_parent_number: BlockNumberFor<T>,
cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
set_go_ahead: SetGoAhead,
enact_upgrade_directly: EnactUpgradeDirectly,
) -> Weight {
let mut weight = Weight::zero();

// Compute the relay-chain block number starting at which the code upgrade is ready to be
// applied.
//
// The first parablock that has a relay-parent higher or at the same height of `expected_at`
// will trigger the code upgrade. The parablock that comes after that will be validated
// against the new validation code.
//
// Here we are trying to choose the block number that will have `validation_upgrade_delay`
// blocks from the relay-parent of inclusion of the the block that scheduled code upgrade
// but no less than `minimum_validation_upgrade_delay`. We want this delay out of caution
// so that when the last vote for pre-checking comes the parachain will have some time until
// the upgrade finally takes place.
let expected_at = cmp::max(
relay_parent_number + cfg.validation_upgrade_delay,
now + cfg.minimum_validation_upgrade_delay,
);
if enact_upgrade_directly == EnactUpgradeDirectly::Yes {
FutureCodeHash::<T>::remove(&id);
weight.saturating_add(Self::set_current_code(id, *code_hash, now));
} else {
// Compute the relay-chain block number starting at which the code upgrade is ready to
// be applied.
//
// The first parablock that has a relay-parent higher or at the same height of
// `expected_at` will trigger the code upgrade. The parablock that comes after that will
// be validated against the new validation code.
//
// Here we are trying to choose the block number that will have
// `validation_upgrade_delay` blocks from the relay-parent of inclusion of the the block
// that scheduled code upgrade but no less than `minimum_validation_upgrade_delay`. We
// want this delay out of caution so that when the last vote for pre-checking comes the
// parachain will have some time until the upgrade finally takes place.
let expected_at = cmp::max(
relay_parent_number + cfg.validation_upgrade_delay,
now + cfg.minimum_validation_upgrade_delay,
);

weight += T::DbWeight::get().reads_writes(1, 4);
FutureCodeUpgrades::<T>::insert(&id, expected_at);
weight += T::DbWeight::get().reads_writes(1, 4);
FutureCodeUpgrades::<T>::insert(&id, expected_at);

// Only set an upcoming upgrade if `GoAhead` signal should be set for the respective para.
if set_go_ahead == SetGoAhead::Yes {
UpcomingUpgrades::<T>::mutate(|upcoming_upgrades| {
let insert_idx = upcoming_upgrades
.binary_search_by_key(&expected_at, |&(_, b)| b)
.unwrap_or_else(|idx| idx);
upcoming_upgrades.insert(insert_idx, (id, expected_at));
});
}

let expected_at = expected_at.saturated_into();
let log = ConsensusLog::ParaScheduleUpgradeCode(id, *code_hash, expected_at);
<frame_system::Pallet<T>>::deposit_log(log.into());
let expected_at = expected_at.saturated_into();
let log = ConsensusLog::ParaScheduleUpgradeCode(id, *code_hash, expected_at);
<frame_system::Pallet<T>>::deposit_log(log.into());
}

weight
}
Expand Down Expand Up @@ -1892,7 +1885,7 @@ impl<T: Config> Pallet<T> {
new_code: ValidationCode,
inclusion_block_number: BlockNumberFor<T>,
cfg: &configuration::HostConfiguration<BlockNumberFor<T>>,
set_go_ahead: SetGoAhead,
enact_upgrade_directly: EnactUpgradeDirectly,
) -> Weight {
let mut weight = T::DbWeight::get().reads(1);

Expand Down Expand Up @@ -1949,7 +1942,11 @@ impl<T: Config> Pallet<T> {
});

weight += Self::kick_off_pvf_check(
PvfCheckCause::Upgrade { id, included_at: inclusion_block_number, set_go_ahead },
PvfCheckCause::Upgrade {
id,
included_at: inclusion_block_number,
enact_upgrade_directly,
},
code_hash,
new_code,
cfg,
Expand Down Expand Up @@ -2061,24 +2058,10 @@ impl<T: Config> Pallet<T> {
log::error!(target: LOG_TARGET, "Missing future code hash for {:?}", &id);
return T::DbWeight::get().reads_writes(3, 1 + 3)
};
let maybe_prior_code_hash = CurrentCodeHash::<T>::get(&id);
CurrentCodeHash::<T>::insert(&id, &new_code_hash);

let log = ConsensusLog::ParaUpgradeCode(id, new_code_hash);
<frame_system::Pallet<T>>::deposit_log(log.into());
let weight = Self::set_current_code(id, new_code_hash, expected_at);

// `now` is only used for registering pruning as part of `fn note_past_code`
let now = <frame_system::Pallet<T>>::block_number();

let weight = if let Some(prior_code_hash) = maybe_prior_code_hash {
Self::note_past_code(id, expected_at, now, prior_code_hash)
} else {
log::error!(target: LOG_TARGET, "Missing prior code hash for para {:?}", &id);
Weight::zero()
};

// add 1 to writes due to heads update.
weight + T::DbWeight::get().reads_writes(3, 1 + 3)
weight + T::DbWeight::get().reads_writes(3, 3)
} else {
T::DbWeight::get().reads_writes(1, 1 + 0)
}
Expand All @@ -2094,6 +2077,34 @@ impl<T: Config> Pallet<T> {
weight.saturating_add(T::OnNewHead::on_new_head(id, &new_head))
}

/// Set the current code for the given parachain.
// `at` for para-triggered replacement is the block number of the relay-chain
// block in whose context the parablock was executed
// (i.e. number of `relay_parent` in the receipt)
pub(crate) fn set_current_code(
id: ParaId,
new_code_hash: ValidationCodeHash,
at: BlockNumberFor<T>,
) -> Weight {
let maybe_prior_code_hash = CurrentCodeHash::<T>::get(&id);
CurrentCodeHash::<T>::insert(&id, &new_code_hash);

let log = ConsensusLog::ParaUpgradeCode(id, new_code_hash);
<frame_system::Pallet<T>>::deposit_log(log.into());

// `now` is only used for registering pruning as part of `fn note_past_code`
let now = <frame_system::Pallet<T>>::block_number();

let weight = if let Some(prior_code_hash) = maybe_prior_code_hash {
Self::note_past_code(id, at, now, prior_code_hash)
} else {
log::error!(target: LOG_TARGET, "Missing prior code hash for para {:?}", &id);
Weight::zero()
};

weight + T::DbWeight::get().writes(1)
}

/// Returns the list of PVFs (aka validation code) that require casting a vote by a validator in
/// the active validator set.
pub(crate) fn pvfs_require_precheck() -> Vec<ValidationCodeHash> {
Expand Down
Loading