Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
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.

1 change: 1 addition & 0 deletions polkadot/runtime/common/src/assigned_slots/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,7 @@ mod tests {
type UnsignedPriority = ParasUnsignedPriority;
type QueueFootprinter = ();
type NextSessionRotation = crate::mock::TestNextSessionRotation;
type OnNewHead = ();
}

impl parachains_shared::Config for Test {}
Expand Down
2 changes: 0 additions & 2 deletions polkadot/runtime/common/src/crowdloan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,8 +441,6 @@ pub mod pallet {
);

NextFundIndex::<T>::put(new_fund_index);
// Add a lock to the para so that the configuration cannot be changed.
T::Registrar::apply_lock(index);

Self::deposit_event(Event::<T>::Created { para_id: index });
Ok(())
Expand Down
1 change: 1 addition & 0 deletions polkadot/runtime/common/src/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ impl paras::Config for Test {
type UnsignedPriority = ParasUnsignedPriority;
type QueueFootprinter = ();
type NextSessionRotation = crate::mock::TestNextSessionRotation;
type OnNewHead = ();
}

parameter_types! {
Expand Down
66 changes: 66 additions & 0 deletions polkadot/runtime/common/src/paras_registrar/migration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.

use super::*;
use frame_support::{
dispatch::GetStorageVersion,
traits::{Contains, OnRuntimeUpgrade, StorageVersion},
};

#[derive(Encode, Decode)]
pub struct ParaInfoV1<Account, Balance> {
manager: Account,
deposit: Balance,
locked: bool,
}

pub struct MigrateToV1<T, UnlockParaIds>(sp_std::marker::PhantomData<(T, UnlockParaIds)>);
impl<T: Config, UnlockParaIds: Contains<ParaId>> OnRuntimeUpgrade
for MigrateToV1<T, UnlockParaIds>
{
fn on_runtime_upgrade() -> Weight {
let onchain_version = Pallet::<T>::on_chain_storage_version();

if onchain_version == 0 {
let mut count = 1u64; // storage version read write
Paras::<T>::translate::<ParaInfoV1<T::AccountId, BalanceOf<T>>, _>(|key, v1| {
count.saturating_inc();
Some(ParaInfo {
manager: v1.manager,
deposit: v1.deposit,
locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) },
})
});

StorageVersion::new(1).put::<Pallet<T>>();
log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count);
T::DbWeight::get().reads_writes(count, count)
} else {
log::info!(target: "runtime::registrar", "Migration did not execute. This probably should be removed");
T::DbWeight::get().reads(1)
}
}

#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
Ok(Vec::new())
}

#[cfg(feature = "try-runtime")]
fn post_upgrade(_: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
Ok(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
//! Pallet to handle parachain registration and related fund management.
//! In essence this is a simple wrapper around `paras`.

pub mod migration;

use frame_support::{
dispatch::DispatchResult,
ensure,
Expand All @@ -35,7 +37,7 @@ use sp_std::{prelude::*, result};
use crate::traits::{OnSwap, Registrar};
pub use pallet::*;
use parity_scale_codec::{Decode, Encode};
use runtime_parachains::paras::ParaKind;
use runtime_parachains::paras::{OnNewHead, ParaKind};
use scale_info::TypeInfo;
use sp_runtime::{
traits::{CheckedSub, Saturating},
Expand All @@ -49,7 +51,15 @@ pub struct ParaInfo<Account, Balance> {
/// The amount reserved by the `manager` account for the registration.
deposit: Balance,
/// Whether the para registration should be locked from being controlled by the manager.
locked: bool,
/// None means the lock is not set, and should be treated as false.
locked: Option<bool>,
}

impl<Account, Balance> ParaInfo<Account, Balance> {
/// Returns if the para is locked.
pub fn is_locked(&self) -> bool {
self.locked.unwrap_or(false)
}
}

type BalanceOf<T> =
Expand Down Expand Up @@ -96,8 +106,12 @@ pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);

#[pallet::pallet]
#[pallet::without_storage_info]
#[pallet::storage_version(STORAGE_VERSION)]
pub struct Pallet<T>(_);

#[pallet::config]
Expand Down Expand Up @@ -446,12 +460,12 @@ impl<T: Config> Registrar for Pallet<T> {

// Apply a lock to the parachain.
fn apply_lock(id: ParaId) {
Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = true));
Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(true)));
}

// Remove a lock from the parachain.
fn remove_lock(id: ParaId) {
Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = false));
Paras::<T>::mutate(id, |x| x.as_mut().map(|info| info.locked = Some(false)));
}

// Register a Para ID under control of `manager`.
Expand Down Expand Up @@ -481,9 +495,7 @@ impl<T: Config> Registrar for Pallet<T> {
);
runtime_parachains::schedule_parathread_upgrade::<T>(id)
.map_err(|_| Error::<T>::CannotUpgrade)?;
// Once a para has upgraded to a parachain, it can no longer be managed by the owner.
// Intentionally, the flag stays with the para even after downgrade.
Self::apply_lock(id);

Ok(())
}

Expand Down Expand Up @@ -533,7 +545,7 @@ impl<T: Config> Pallet<T> {
.map_err(|e| e.into())
.and_then(|who| -> DispatchResult {
let para_info = Paras::<T>::get(id).ok_or(Error::<T>::NotRegistered)?;
ensure!(!para_info.locked, Error::<T>::ParaLocked);
ensure!(!para_info.is_locked(), Error::<T>::ParaLocked);
ensure!(para_info.manager == who, Error::<T>::NotOwner);
Ok(())
})
Expand Down Expand Up @@ -566,7 +578,7 @@ impl<T: Config> Pallet<T> {

let deposit = deposit_override.unwrap_or_else(T::ParaDeposit::get);
<T as Config>::Currency::reserve(&who, deposit)?;
let info = ParaInfo { manager: who.clone(), deposit, locked: false };
let info = ParaInfo { manager: who.clone(), deposit, locked: None };

Paras::<T>::insert(id, info);
Self::deposit_event(Event::<T>::Reserved { para_id: id, who });
Expand All @@ -585,7 +597,7 @@ impl<T: Config> Pallet<T> {
) -> DispatchResult {
let deposited = if let Some(para_data) = Paras::<T>::get(id) {
ensure!(para_data.manager == who, Error::<T>::NotOwner);
ensure!(!para_data.locked, Error::<T>::ParaLocked);
ensure!(!para_data.is_locked(), Error::<T>::ParaLocked);
para_data.deposit
} else {
ensure!(!ensure_reserved, Error::<T>::NotReserved);
Expand All @@ -601,7 +613,7 @@ impl<T: Config> Pallet<T> {
} else if let Some(rebate) = deposited.checked_sub(&deposit) {
<T as Config>::Currency::unreserve(&who, rebate);
};
let info = ParaInfo { manager: who.clone(), deposit, locked: false };
let info = ParaInfo { manager: who.clone(), deposit, locked: None };

Paras::<T>::insert(id, info);
// We check above that para has no lifecycle, so this should not fail.
Expand Down Expand Up @@ -665,6 +677,21 @@ impl<T: Config> Pallet<T> {
}
}

impl<T: Config> OnNewHead for Pallet<T> {
fn on_new_head(id: ParaId, _head: &HeadData) -> Weight {
// mark the parachain locked if the locked value is not already set
let mut writes = 0;
if let Some(info) = Paras::<T>::get(id) {
if info.locked.is_none() {
info.locked = Some(true);
Paras::<T>::insert(id, info);
writes += 1;
}
}
T::DbWeight::get().reads_writes(1, writes)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -784,6 +811,7 @@ mod tests {
type UnsignedPriority = ParasUnsignedPriority;
type QueueFootprinter = ();
type NextSessionRotation = crate::mock::TestNextSessionRotation;
type OnNewHead = ();
}

impl configuration::Config for Test {
Expand Down Expand Up @@ -1270,8 +1298,10 @@ mod tests {
));

assert_noop!(Registrar::add_lock(RuntimeOrigin::signed(2), para_id), BadOrigin);
// Once they begin onboarding, we lock them in.
assert_ok!(Registrar::add_lock(RuntimeOrigin::signed(1), para_id));

// Once they produces new block, we lock them in.
Registrar::on_new_head(para_id, &Default::default());

// Owner cannot pass origin check when checking lock
assert_noop!(
Registrar::ensure_root_para_or_owner(RuntimeOrigin::signed(1), para_id),
Expand All @@ -1283,6 +1313,11 @@ mod tests {
assert_ok!(Registrar::remove_lock(para_origin(para_id), para_id));
// Owner can pass origin check again
assert_ok!(Registrar::ensure_root_para_or_owner(RuntimeOrigin::signed(1), para_id));

// Won't lock again after it is unlocked
Registrar::on_new_head(para_id, &Default::default());

assert_ok!(Registrar::ensure_root_para_or_owner(RuntimeOrigin::signed(1), para_id));
});
}

Expand Down
17 changes: 17 additions & 0 deletions polkadot/runtime/kusama/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,7 @@ impl parachains_paras::Config for Runtime {
type UnsignedPriority = ParasUnsignedPriority;
type QueueFootprinter = ParaInclusion;
type NextSessionRotation = Babe;
type OnNewHead = Registrar;
}

parameter_types! {
Expand Down Expand Up @@ -1712,6 +1713,19 @@ pub mod migrations {
}
}

pub struct ParachainsToUnlock;
impl Contains<ParaId> for ParachainsToUnlock {
fn contains(id: &ParaId) -> bool {
let id: u32 = (*id).into();
// ksuama parachains/parathreads that are locked and never produced block
match id {
2003 | 2077 | 2089 | 2111 | 2112 | 2127 | 2130 | 2224 | 2231 | 2233 | 2237 |
2256 | 2257 | 2258 | 2261 | 2268 | 2274 | 2275 => true,
_ => false,
}
}
}

/// Unreleased migrations. Add new ones here:
pub type Unreleased = (
init_state_migration::InitMigrate,
Expand Down Expand Up @@ -1741,6 +1755,9 @@ pub mod migrations {

// Upgrade SessionKeys to include BEEFY key
UpgradeSessionKeys,

// Migrate parachain info format
paras_registrar::migration::MigrateToV1<Runtime, ParachainsToUnlock>,
);
}

Expand Down
1 change: 1 addition & 0 deletions polkadot/runtime/parachains/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition.workspace = true
license.workspace = true

[dependencies]
impl-trait-for-tuples = "0.2.2"
bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] }
parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] }
log = { version = "0.4.17", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions polkadot/runtime/parachains/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ impl crate::paras::Config for Test {
type UnsignedPriority = ParasUnsignedPriority;
type QueueFootprinter = ParaInclusion;
type NextSessionRotation = TestNextSessionRotation;
type OnNewHead = ();
}

impl crate::dmp::Config for Test {}
Expand Down
27 changes: 24 additions & 3 deletions polkadot/runtime/parachains/src/paras/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,22 @@ impl<BlockNumber> PvfCheckActiveVoteState<BlockNumber> {
}
}

/// Runtime hook for when a parachain head is updated.
pub trait OnNewHead {
/// Called when a parachain head is updated.
/// Returns the weight consumed by this function.
fn on_new_head(id: ParaId, head: &HeadData) -> Weight;
}

#[impl_trait_for_tuples::impl_for_tuples(30)]
impl OnNewHead for Tuple {
fn on_new_head(id: ParaId, head: &HeadData) -> Weight {
let mut weight: Weight = Default::default();
for_tuples!( #( weight.saturating_accrue(Tuple::on_new_head(id, head)); )* );
weight
}
}

pub trait WeightInfo {
fn force_set_current_code(c: u32) -> Weight;
fn force_set_current_head(s: u32) -> Weight;
Expand Down Expand Up @@ -575,6 +591,9 @@ pub mod pallet {
/// be set to the `ParaInclusion` pallet.
type QueueFootprinter: QueueFootprinter<Origin = UmpQueueId>;

/// Runtime hook for when a parachain head is updated.
type OnNewHead: OnNewHead;

/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
}
Expand Down Expand Up @@ -1962,10 +1981,10 @@ impl<T: Config> Pallet<T> {
new_head: HeadData,
execution_context: BlockNumberFor<T>,
) -> Weight {
Heads::<T>::insert(&id, new_head);
Heads::<T>::insert(&id, &new_head);
MostRecentContext::<T>::insert(&id, execution_context);

if let Some(expected_at) = FutureCodeUpgrades::<T>::get(&id) {
let weight = if let Some(expected_at) = FutureCodeUpgrades::<T>::get(&id) {
if expected_at <= execution_context {
FutureCodeUpgrades::<T>::remove(&id);
UpgradeGoAheadSignal::<T>::remove(&id);
Expand Down Expand Up @@ -2005,7 +2024,9 @@ impl<T: Config> Pallet<T> {
// the `Abort` signal.
UpgradeGoAheadSignal::<T>::remove(&id);
T::DbWeight::get().reads_writes(1, 2)
}
};

weight.saturating_add(T::OnNewHead::on_new_head(id, &new_head))
}

/// Returns the list of PVFs (aka validation code) that require casting a vote by a validator in
Expand Down
Loading