Skip to content

Commit cd71f7e

Browse files
xlcbkchrggwpez
authored
RFC 14: Improve locking mechanism for parachains (#1290)
* rfc14 * Update polkadot/runtime/common/src/paras_registrar/mod.rs Co-authored-by: Bastian Köcher <[email protected]> * Update polkadot/runtime/common/src/paras_registrar/mod.rs Co-authored-by: Bastian Köcher <[email protected]> * Update polkadot/runtime/common/src/paras_registrar/mod.rs Co-authored-by: Bastian Köcher <[email protected]> * fmt * fix * Update polkadot/runtime/common/src/paras_registrar/migration.rs Co-authored-by: Oliver Tale-Yazdi <[email protected]> * fmt * 2224 is unlocked * update migration list * update comment * use VersionedMigration --------- Co-authored-by: Bastian Köcher <[email protected]> Co-authored-by: Oliver Tale-Yazdi <[email protected]>
1 parent eaf380a commit cd71f7e

File tree

18 files changed

+191
-24
lines changed

18 files changed

+191
-24
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

polkadot/runtime/common/src/assigned_slots/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,7 @@ mod tests {
739739
type UnsignedPriority = ParasUnsignedPriority;
740740
type QueueFootprinter = ();
741741
type NextSessionRotation = crate::mock::TestNextSessionRotation;
742+
type OnNewHead = ();
742743
}
743744

744745
impl parachains_shared::Config for Test {}

polkadot/runtime/common/src/crowdloan/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,6 @@ pub mod pallet {
441441
);
442442

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

447445
Self::deposit_event(Event::<T>::Created { para_id: index });
448446
Ok(())

polkadot/runtime/common/src/integration_tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ impl paras::Config for Test {
204204
type UnsignedPriority = ParasUnsignedPriority;
205205
type QueueFootprinter = ();
206206
type NextSessionRotation = crate::mock::TestNextSessionRotation;
207+
type OnNewHead = ();
207208
}
208209

209210
parameter_types! {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright (C) Parity Technologies (UK) Ltd.
2+
// This file is part of Polkadot.
3+
4+
// Polkadot is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
9+
// Polkadot is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License
15+
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
16+
17+
use super::*;
18+
use frame_support::traits::{Contains, OnRuntimeUpgrade};
19+
20+
#[derive(Encode, Decode)]
21+
pub struct ParaInfoV1<Account, Balance> {
22+
manager: Account,
23+
deposit: Balance,
24+
locked: bool,
25+
}
26+
27+
pub struct VersionUncheckedMigrateToV1<T, UnlockParaIds>(
28+
sp_std::marker::PhantomData<(T, UnlockParaIds)>,
29+
);
30+
impl<T: Config, UnlockParaIds: Contains<ParaId>> OnRuntimeUpgrade
31+
for VersionUncheckedMigrateToV1<T, UnlockParaIds>
32+
{
33+
fn on_runtime_upgrade() -> Weight {
34+
let mut count = 0u64;
35+
Paras::<T>::translate::<ParaInfoV1<T::AccountId, BalanceOf<T>>, _>(|key, v1| {
36+
count.saturating_inc();
37+
Some(ParaInfo {
38+
manager: v1.manager,
39+
deposit: v1.deposit,
40+
locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) },
41+
})
42+
});
43+
44+
log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count);
45+
T::DbWeight::get().reads_writes(count, count)
46+
}
47+
48+
#[cfg(feature = "try-runtime")]
49+
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
50+
Ok((Paras::<T>::iter_keys().count() as u32).encode())
51+
}
52+
53+
#[cfg(feature = "try-runtime")]
54+
fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
55+
let old_count = u32::decode(&mut &state[..]).expect("Known good");
56+
let new_count = Paras::<T>::iter_values().count() as u32;
57+
58+
ensure!(old_count == new_count, "Paras count should not change");
59+
Ok(())
60+
}
61+
}
62+
63+
#[cfg(feature = "experimental")]
64+
pub type VersionCheckedMigrateToV1<T, UnlockParaIds> =
65+
frame_support::migrations::VersionedMigration<
66+
0,
67+
1,
68+
VersionUncheckedMigrateToV1<T, UnlockParaIds>,
69+
super::Pallet<T>,
70+
<T as frame_system::Config>::DbWeight,
71+
>;

polkadot/runtime/common/src/paras_registrar.rs renamed to polkadot/runtime/common/src/paras_registrar/mod.rs

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
//! Pallet to handle parachain registration and related fund management.
1818
//! In essence this is a simple wrapper around `paras`.
1919
20+
pub mod migration;
21+
2022
use frame_support::{
2123
dispatch::DispatchResult,
2224
ensure,
@@ -35,7 +37,7 @@ use sp_std::{prelude::*, result};
3537
use crate::traits::{OnSwap, Registrar};
3638
pub use pallet::*;
3739
use parity_scale_codec::{Decode, Encode};
38-
use runtime_parachains::paras::ParaKind;
40+
use runtime_parachains::paras::{OnNewHead, ParaKind};
3941
use scale_info::TypeInfo;
4042
use sp_runtime::{
4143
traits::{CheckedSub, Saturating},
@@ -49,7 +51,15 @@ pub struct ParaInfo<Account, Balance> {
4951
/// The amount reserved by the `manager` account for the registration.
5052
deposit: Balance,
5153
/// Whether the para registration should be locked from being controlled by the manager.
52-
locked: bool,
54+
/// None means the lock had not been explicitly set, and should be treated as false.
55+
locked: Option<bool>,
56+
}
57+
58+
impl<Account, Balance> ParaInfo<Account, Balance> {
59+
/// Returns if the para is locked.
60+
pub fn is_locked(&self) -> bool {
61+
self.locked.unwrap_or(false)
62+
}
5363
}
5464

5565
type BalanceOf<T> =
@@ -96,8 +106,12 @@ pub mod pallet {
96106
use frame_support::pallet_prelude::*;
97107
use frame_system::pallet_prelude::*;
98108

109+
/// The current storage version.
110+
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
111+
99112
#[pallet::pallet]
100113
#[pallet::without_storage_info]
114+
#[pallet::storage_version(STORAGE_VERSION)]
101115
pub struct Pallet<T>(_);
102116

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

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

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

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

@@ -533,7 +545,7 @@ impl<T: Config> Pallet<T> {
533545
.map_err(|e| e.into())
534546
.and_then(|who| -> DispatchResult {
535547
let para_info = Paras::<T>::get(id).ok_or(Error::<T>::NotRegistered)?;
536-
ensure!(!para_info.locked, Error::<T>::ParaLocked);
548+
ensure!(!para_info.is_locked(), Error::<T>::ParaLocked);
537549
ensure!(para_info.manager == who, Error::<T>::NotOwner);
538550
Ok(())
539551
})
@@ -566,7 +578,7 @@ impl<T: Config> Pallet<T> {
566578

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

571583
Paras::<T>::insert(id, info);
572584
Self::deposit_event(Event::<T>::Reserved { para_id: id, who });
@@ -585,7 +597,7 @@ impl<T: Config> Pallet<T> {
585597
) -> DispatchResult {
586598
let deposited = if let Some(para_data) = Paras::<T>::get(id) {
587599
ensure!(para_data.manager == who, Error::<T>::NotOwner);
588-
ensure!(!para_data.locked, Error::<T>::ParaLocked);
600+
ensure!(!para_data.is_locked(), Error::<T>::ParaLocked);
589601
para_data.deposit
590602
} else {
591603
ensure!(!ensure_reserved, Error::<T>::NotReserved);
@@ -601,7 +613,7 @@ impl<T: Config> Pallet<T> {
601613
} else if let Some(rebate) = deposited.checked_sub(&deposit) {
602614
<T as Config>::Currency::unreserve(&who, rebate);
603615
};
604-
let info = ParaInfo { manager: who.clone(), deposit, locked: false };
616+
let info = ParaInfo { manager: who.clone(), deposit, locked: None };
605617

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

680+
impl<T: Config> OnNewHead for Pallet<T> {
681+
fn on_new_head(id: ParaId, _head: &HeadData) -> Weight {
682+
// mark the parachain locked if the locked value is not already set
683+
let mut writes = 0;
684+
if let Some(mut info) = Paras::<T>::get(id) {
685+
if info.locked.is_none() {
686+
info.locked = Some(true);
687+
Paras::<T>::insert(id, info);
688+
writes += 1;
689+
}
690+
}
691+
T::DbWeight::get().reads_writes(1, writes)
692+
}
693+
}
694+
668695
#[cfg(test)]
669696
mod tests {
670697
use super::*;
@@ -784,6 +811,7 @@ mod tests {
784811
type UnsignedPriority = ParasUnsignedPriority;
785812
type QueueFootprinter = ();
786813
type NextSessionRotation = crate::mock::TestNextSessionRotation;
814+
type OnNewHead = ();
787815
}
788816

789817
impl configuration::Config for Test {
@@ -1270,8 +1298,10 @@ mod tests {
12701298
));
12711299

12721300
assert_noop!(Registrar::add_lock(RuntimeOrigin::signed(2), para_id), BadOrigin);
1273-
// Once they begin onboarding, we lock them in.
1274-
assert_ok!(Registrar::add_lock(RuntimeOrigin::signed(1), para_id));
1301+
1302+
// Once they produces new block, we lock them in.
1303+
Registrar::on_new_head(para_id, &Default::default());
1304+
12751305
// Owner cannot pass origin check when checking lock
12761306
assert_noop!(
12771307
Registrar::ensure_root_para_or_owner(RuntimeOrigin::signed(1), para_id),
@@ -1283,6 +1313,11 @@ mod tests {
12831313
assert_ok!(Registrar::remove_lock(para_origin(para_id), para_id));
12841314
// Owner can pass origin check again
12851315
assert_ok!(Registrar::ensure_root_para_or_owner(RuntimeOrigin::signed(1), para_id));
1316+
1317+
// Won't lock again after it is unlocked
1318+
Registrar::on_new_head(para_id, &Default::default());
1319+
1320+
assert_ok!(Registrar::ensure_root_para_or_owner(RuntimeOrigin::signed(1), para_id));
12861321
});
12871322
}
12881323

polkadot/runtime/kusama/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ frame-system-benchmarking = { path = "../../../substrate/frame/system/benchmarki
103103
pallet-election-provider-support-benchmarking = { path = "../../../substrate/frame/election-provider-support/benchmarking", default-features = false, optional = true }
104104
hex-literal = "0.4.1"
105105

106-
runtime-common = { package = "polkadot-runtime-common", path = "../common", default-features = false }
106+
runtime-common = { package = "polkadot-runtime-common", path = "../common", default-features = false, features = ["experimental"] }
107107
runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parachains", default-features = false }
108108
primitives = { package = "polkadot-primitives", path = "../../primitives", default-features = false }
109109

polkadot/runtime/kusama/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,7 @@ impl parachains_paras::Config for Runtime {
12151215
type UnsignedPriority = ParasUnsignedPriority;
12161216
type QueueFootprinter = ParaInclusion;
12171217
type NextSessionRotation = Babe;
1218+
type OnNewHead = Registrar;
12181219
}
12191220

12201221
parameter_types! {
@@ -1710,6 +1711,19 @@ pub mod migrations {
17101711
}
17111712
}
17121713

1714+
pub struct ParachainsToUnlock;
1715+
impl Contains<ParaId> for ParachainsToUnlock {
1716+
fn contains(id: &ParaId) -> bool {
1717+
let id: u32 = (*id).into();
1718+
// ksuama parachains/parathreads that are locked and never produced block
1719+
match id {
1720+
2003 | 2008 | 2018 | 2077 | 2089 | 2111 | 2112 | 2120 | 2126 | 2127 | 2130 |
1721+
2226 | 2227 | 2231 | 2233 | 2237 | 2256 | 2257 | 2261 | 2268 | 2275 => true,
1722+
_ => false,
1723+
}
1724+
}
1725+
}
1726+
17131727
/// Unreleased migrations. Add new ones here:
17141728
pub type Unreleased = (
17151729
init_state_migration::InitMigrate,
@@ -1741,6 +1755,8 @@ pub mod migrations {
17411755
UpgradeSessionKeys,
17421756

17431757
parachains_configuration::migration::v9::MigrateToV9<Runtime>,
1758+
// Migrate parachain info format
1759+
paras_registrar::migration::VersionCheckedMigrateToV1<Runtime, ParachainsToUnlock>,
17441760
);
17451761
}
17461762

polkadot/runtime/parachains/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition.workspace = true
66
license.workspace = true
77

88
[dependencies]
9+
impl-trait-for-tuples = "0.2.2"
910
bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] }
1011
parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] }
1112
log = { version = "0.4.17", default-features = false }

polkadot/runtime/parachains/src/mock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ impl crate::paras::Config for Test {
214214
type UnsignedPriority = ParasUnsignedPriority;
215215
type QueueFootprinter = ParaInclusion;
216216
type NextSessionRotation = TestNextSessionRotation;
217+
type OnNewHead = ();
217218
}
218219

219220
impl crate::dmp::Config for Test {}

0 commit comments

Comments
 (0)