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

Manual Para Lock #5451

Merged
merged 14 commits into from
Oct 11, 2022
119 changes: 92 additions & 27 deletions runtime/common/src/paras_registrar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ pub trait WeightInfo {
fn force_register() -> Weight;
fn deregister() -> Weight;
fn swap() -> Weight;
fn schedule_code_upgrade(b: u32) -> Weight;
fn set_current_head(b: u32) -> Weight;
}

pub struct TestWeightInfo;
Expand All @@ -79,6 +81,12 @@ impl WeightInfo for TestWeightInfo {
fn swap() -> Weight {
Weight::zero()
}
fn schedule_code_upgrade(_b: u32) -> Weight {
Weight::zero()
}
fn set_current_head(_b: u32) -> Weight {
Weight::zero()
}
}

#[frame_support::pallet]
Expand Down Expand Up @@ -318,11 +326,11 @@ pub mod pallet {
/// Remove a manager lock from a para. This will allow the manager of a
/// previously locked para to deregister or swap a para without using governance.
///
/// Can only be called by the Root origin.
/// Can only be called by the Root origin or the parachain.
#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
pub fn force_remove_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
ensure_root(origin)?;
Self::remove_lock(para);
pub fn remove_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
Self::ensure_root_or_para(origin, para)?;
<Self as Registrar>::remove_lock(para);
Ok(())
}

Expand All @@ -348,6 +356,45 @@ pub mod pallet {
NextFreeParaId::<T>::set(id + 1);
Ok(())
}

/// Add a manager lock from a para. This will prevent the manager of a
/// para to deregister or swap a para.
///
/// Can be called by Root, the parachain, or the parachain manager if the parachain is unlocked.
#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
pub fn add_lock(origin: OriginFor<T>, para: ParaId) -> DispatchResult {
Self::ensure_root_para_or_owner(origin, para)?;
<Self as Registrar>::apply_lock(para);
Ok(())
}
Comment on lines +365 to +369
Copy link
Member

Choose a reason for hiding this comment

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

We somehow need to tell people to use this then.

Copy link
Member

Choose a reason for hiding this comment

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

Do I understand correctly that a user has no guarantee that the chain will ever become trustless?
Maybe we can add a block number to the config so that after that block the chain can be locked by anyone.

Copy link
Member

Choose a reason for hiding this comment

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

I mean that is not our job nor the job of the relay chain to ensure that a chain is trustless. Maybe this is some very specific chain controlled by one entity. I think it would be fine if this entity keeps control all the time. I mean at some point there is maybe one parachain that controlls another parachain. If the first parachain is trustless, the second one is trustless as well :P

Copy link
Member

Choose a reason for hiding this comment

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

Ah right, we cant make assumptions about the origin. So if its already trust-less, then its not an issue.

Copy link
Member Author

Choose a reason for hiding this comment

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

My take on this is that we need to tell the UIs to put a "warning" flag on parachains which are unlocked, which inform users of the power that these parachains keep centralized. If the parachains want to remove this UI element, they will call this function.


/// Schedule a parachain upgrade.
///
/// Can be called by Root, the parachain, or the parachain manager if the parachain is unlocked.
#[pallet::weight(<T as Config>::WeightInfo::schedule_code_upgrade(new_code.0.len() as u32))]
pub fn schedule_code_upgrade(
origin: OriginFor<T>,
para: ParaId,
new_code: ValidationCode,
) -> DispatchResult {
Self::ensure_root_para_or_owner(origin, para)?;
runtime_parachains::schedule_code_upgrade::<T>(para, new_code)?;
Ok(())
}

/// Set the parachain's current head.
///
/// Can be called by Root, the parachain, or the parachain manager if the parachain is unlocked.
#[pallet::weight(<T as Config>::WeightInfo::set_current_head(new_head.0.len() as u32))]
pub fn set_current_head(
Copy link
Member

Choose a reason for hiding this comment

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

This function is not tested, only the ensure_root_para_or_owner.

origin: OriginFor<T>,
para: ParaId,
new_head: HeadData,
) -> DispatchResult {
Self::ensure_root_para_or_owner(origin, para)?;
runtime_parachains::set_current_head::<T>(para, new_head);
Ok(())
}
}
}

Expand Down Expand Up @@ -379,7 +426,7 @@ impl<T: Config> Registrar for Pallet<T> {
Paras::<T>::mutate(id, |x| x.as_mut().map(|mut info| info.locked = true));
}

// Apply a lock to the parachain.
// Remove a lock from the parachain.
fn remove_lock(id: ParaId) {
Paras::<T>::mutate(id, |x| x.as_mut().map(|mut info| info.locked = false));
}
Expand Down Expand Up @@ -467,17 +514,23 @@ impl<T: Config> Pallet<T> {
ensure!(para_info.manager == who, Error::<T>::NotOwner);
Ok(())
})
.or_else(|_| -> DispatchResult {
// Else check if para origin...
let caller_id =
ensure_parachain(<T as Config>::RuntimeOrigin::from(origin.clone()))?;
ensure!(caller_id == id, Error::<T>::NotOwner);
Ok(())
})
.or_else(|_| -> DispatchResult {
// Check if root...
ensure_root(origin.clone()).map_err(|e| e.into())
})
.or_else(|_| -> DispatchResult { Self::ensure_root_or_para(origin, id) })
}

/// Ensure the origin is one of Root or the `para` itself.
fn ensure_root_or_para(
shawntabrizi marked this conversation as resolved.
Show resolved Hide resolved
origin: <T as frame_system::Config>::RuntimeOrigin,
id: ParaId,
) -> DispatchResult {
if let Ok(caller_id) = ensure_parachain(<T as Config>::RuntimeOrigin::from(origin.clone()))
{
// Check if matching para id...
ensure!(caller_id == id, Error::<T>::NotOwner);
} else {
// Check if root...
ensure_root(origin.clone())?;
}
Ok(())
}

fn do_reserve(
Expand Down Expand Up @@ -1087,21 +1140,20 @@ mod tests {
vec![1, 2, 3].into(),
));

// Owner can call swap
assert_ok!(Registrar::swap(RuntimeOrigin::signed(1), para_id, para_id + 1));

// 2 session changes to fully onboard.
run_to_session(2);
assert_eq!(Parachains::lifecycle(para_id), Some(ParaLifecycle::Parathread));

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

// Owner cannot call swap anymore
assert_ok!(Registrar::add_lock(RuntimeOrigin::signed(1), para_id));
// Owner cannot pass origin check when checking lock
assert_noop!(
Registrar::swap(RuntimeOrigin::signed(1), para_id, para_id + 2),
Registrar::ensure_root_para_or_owner(RuntimeOrigin::signed(1), para_id),
BadOrigin
);
// Owner cannot remove lock.
assert_noop!(Registrar::remove_lock(RuntimeOrigin::signed(1), para_id), BadOrigin);
// Para can.
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));
});
}

Expand Down Expand Up @@ -1227,6 +1279,7 @@ mod benchmarking {
use crate::traits::Registrar as RegistrarT;
use frame_support::assert_ok;
use frame_system::RawOrigin;
use primitives::v2::{MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE};
use runtime_parachains::{paras, shared, Origin as ParaOrigin};
use sp_runtime::traits::Bounded;

Expand Down Expand Up @@ -1343,6 +1396,18 @@ mod benchmarking {
assert_eq!(paras::Pallet::<T>::lifecycle(parathread), Some(ParaLifecycle::Parachain));
}

schedule_code_upgrade {
let b in 1 .. MAX_CODE_SIZE;
let new_code = ValidationCode(vec![0; b as usize]);
let para_id = ParaId::from(1000);
}: _(RawOrigin::Root, para_id, new_code)

set_current_head {
let b in 1 .. MAX_HEAD_DATA_SIZE;
let new_head = HeadData(vec![0; b as usize]);
let para_id = ParaId::from(1000);
}: _(RawOrigin::Root, para_id, new_head)

impl_benchmark_test_suite!(
Registrar,
crate::integration_tests::new_test_ext(),
Expand Down
24 changes: 24 additions & 0 deletions runtime/kusama/src/weights/runtime_common_paras_registrar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,28 @@ impl<T: frame_system::Config> runtime_common::paras_registrar::WeightInfo for We
.saturating_add(T::DbWeight::get().reads(10 as u64))
.saturating_add(T::DbWeight::get().writes(8 as u64))
}
// Storage: Paras FutureCodeHash (r:1 w:1)
// Storage: Paras CurrentCodeHash (r:1 w:0)
// Storage: Paras UpgradeCooldowns (r:1 w:1)
// Storage: Paras PvfActiveVoteMap (r:1 w:0)
// Storage: Paras CodeByHash (r:1 w:1)
// Storage: Paras UpcomingUpgrades (r:1 w:1)
// Storage: System Digest (r:1 w:1)
// Storage: Paras CodeByHashRefs (r:1 w:1)
// Storage: Paras FutureCodeUpgrades (r:0 w:1)
// Storage: Paras UpgradeRestrictionSignal (r:0 w:1)
fn schedule_code_upgrade(b: u32, ) -> Weight {
Weight::from_ref_time(0 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(3_000 as u64).saturating_mul(b as u64))
.saturating_add(T::DbWeight::get().reads(8 as u64))
.saturating_add(T::DbWeight::get().writes(8 as u64))
}
// Storage: Paras Heads (r:0 w:1)
fn set_current_head(b: u32, ) -> Weight {
Weight::from_ref_time(5_494_000 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(1_000 as u64).saturating_mul(b as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
}
20 changes: 19 additions & 1 deletion runtime/parachains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ mod mock;

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

/// Schedule a para to be initialized at the start of the next session with the given genesis data.
///
Expand Down Expand Up @@ -78,3 +79,20 @@ pub fn schedule_parathread_upgrade<T: paras::Config>(id: ParaId) -> Result<(), (
pub fn schedule_parachain_downgrade<T: paras::Config>(id: ParaId) -> Result<(), ()> {
paras::Pallet::<T>::schedule_parachain_downgrade(id).map_err(|_| ())
}

/// Schedules a validation code upgrade to a parachain with the given id.
///
/// This simply calls [`crate::paras::Pallet::schedule_code_upgrade_external`].
pub fn schedule_code_upgrade<T: paras::Config>(
id: ParaId,
new_code: ValidationCode,
) -> DispatchResult {
paras::Pallet::<T>::schedule_code_upgrade_external(id, new_code)
}

/// Sets the current parachain head with the given id.
///
/// This simply calls [`crate::paras::Pallet::set_current_head`].
pub fn set_current_head<T: paras::Config>(id: ParaId, new_head: HeadData) {
paras::Pallet::<T>::set_current_head(id, new_head)
}
32 changes: 29 additions & 3 deletions runtime/parachains/src/paras/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ use primitives::v2::{
use scale_info::TypeInfo;
use sp_core::RuntimeDebug;
use sp_runtime::{
traits::{AppVerify, One},
traits::{AppVerify, One, Saturating},
DispatchResult, SaturatedConversion,
};
use sp_std::{cmp, mem, prelude::*};
Expand Down Expand Up @@ -535,6 +535,8 @@ pub mod pallet {
/// The PVF pre-checking statement cannot be included since the PVF pre-checking mechanism
/// is disabled.
PvfCheckDisabled,
/// Parachain cannot currently schedule a code upgrade.
CannotUpgradeCode,
}

/// All currently active PVF pre-checking votes.
Expand Down Expand Up @@ -752,8 +754,7 @@ pub mod pallet {
new_head: HeadData,
) -> DispatchResult {
ensure_root(origin)?;
<Self as Store>::Heads::insert(&para, new_head);
Self::deposit_event(Event::CurrentHeadUpdated(para));
Self::set_current_head(para, new_head);
Ok(())
}

Expand Down Expand Up @@ -1050,6 +1051,31 @@ const INVALID_TX_DOUBLE_VOTE: u8 = 3;
const INVALID_TX_PVF_CHECK_DISABLED: u8 = 4;

impl<T: Config> Pallet<T> {
/// This is a call to schedule code upgrades for parachains which is safe to be called
/// outside of this module. That means this function does all checks necessary to ensure
/// that some external code is allowed to trigger a code upgrade. We do not do auth checks,
/// that should be handled by whomever calls this function.
pub(crate) fn schedule_code_upgrade_external(
id: ParaId,
new_code: ValidationCode,
) -> DispatchResult {
// Check that we can schedule an upgrade at all.
ensure!(Self::can_upgrade_validation_code(id), Error::<T>::CannotUpgradeCode);
let config = configuration::Pallet::<T>::config();
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);
Self::deposit_event(Event::CodeUpgradeScheduled(id));
Ok(())
}

/// Set the current head of a parachain.
pub(crate) fn set_current_head(para: ParaId, new_head: HeadData) {
<Self as Store>::Heads::insert(&para, new_head);
Self::deposit_event(Event::CurrentHeadUpdated(para));
}

/// Called by the initializer to initialize the paras pallet.
pub(crate) fn initializer_initialize(now: T::BlockNumber) -> Weight {
let weight = Self::prune_old_code(now);
Expand Down
24 changes: 24 additions & 0 deletions runtime/polkadot/src/weights/runtime_common_paras_registrar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,28 @@ impl<T: frame_system::Config> runtime_common::paras_registrar::WeightInfo for We
.saturating_add(T::DbWeight::get().reads(10 as u64))
.saturating_add(T::DbWeight::get().writes(8 as u64))
}
// Storage: Paras FutureCodeHash (r:1 w:1)
// Storage: Paras CurrentCodeHash (r:1 w:0)
// Storage: Paras UpgradeCooldowns (r:1 w:1)
// Storage: Paras PvfActiveVoteMap (r:1 w:0)
// Storage: Paras CodeByHash (r:1 w:1)
// Storage: Paras UpcomingUpgrades (r:1 w:1)
// Storage: System Digest (r:1 w:1)
// Storage: Paras CodeByHashRefs (r:1 w:1)
// Storage: Paras FutureCodeUpgrades (r:0 w:1)
// Storage: Paras UpgradeRestrictionSignal (r:0 w:1)
fn schedule_code_upgrade(b: u32, ) -> Weight {
Weight::from_ref_time(0 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(3_000 as u64).saturating_mul(b as u64))
.saturating_add(T::DbWeight::get().reads(8 as u64))
.saturating_add(T::DbWeight::get().writes(8 as u64))
}
// Storage: Paras Heads (r:0 w:1)
fn set_current_head(b: u32, ) -> Weight {
Weight::from_ref_time(5_494_000 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(1_000 as u64).saturating_mul(b as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
}
24 changes: 24 additions & 0 deletions runtime/rococo/src/weights/runtime_common_paras_registrar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,28 @@ impl<T: frame_system::Config> runtime_common::paras_registrar::WeightInfo for We
.saturating_add(T::DbWeight::get().reads(10 as u64))
.saturating_add(T::DbWeight::get().writes(8 as u64))
}
// Storage: Paras FutureCodeHash (r:1 w:1)
// Storage: Paras CurrentCodeHash (r:1 w:0)
// Storage: Paras UpgradeCooldowns (r:1 w:1)
// Storage: Paras PvfActiveVoteMap (r:1 w:0)
// Storage: Paras CodeByHash (r:1 w:1)
// Storage: Paras UpcomingUpgrades (r:1 w:1)
// Storage: System Digest (r:1 w:1)
// Storage: Paras CodeByHashRefs (r:1 w:1)
// Storage: Paras FutureCodeUpgrades (r:0 w:1)
// Storage: Paras UpgradeRestrictionSignal (r:0 w:1)
fn schedule_code_upgrade(b: u32, ) -> Weight {
Weight::from_ref_time(0 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(3_000 as u64).saturating_mul(b as u64))
.saturating_add(T::DbWeight::get().reads(8 as u64))
.saturating_add(T::DbWeight::get().writes(8 as u64))
}
// Storage: Paras Heads (r:0 w:1)
fn set_current_head(b: u32, ) -> Weight {
Weight::from_ref_time(5_494_000 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(1_000 as u64).saturating_mul(b as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
}
24 changes: 24 additions & 0 deletions runtime/westend/src/weights/runtime_common_paras_registrar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,28 @@ impl<T: frame_system::Config> runtime_common::paras_registrar::WeightInfo for We
.saturating_add(T::DbWeight::get().reads(10 as u64))
.saturating_add(T::DbWeight::get().writes(8 as u64))
}
// Storage: Paras FutureCodeHash (r:1 w:1)
// Storage: Paras CurrentCodeHash (r:1 w:0)
// Storage: Paras UpgradeCooldowns (r:1 w:1)
// Storage: Paras PvfActiveVoteMap (r:1 w:0)
// Storage: Paras CodeByHash (r:1 w:1)
// Storage: Paras UpcomingUpgrades (r:1 w:1)
// Storage: System Digest (r:1 w:1)
// Storage: Paras CodeByHashRefs (r:1 w:1)
// Storage: Paras FutureCodeUpgrades (r:0 w:1)
// Storage: Paras UpgradeRestrictionSignal (r:0 w:1)
fn schedule_code_upgrade(b: u32, ) -> Weight {
Weight::from_ref_time(0 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(3_000 as u64).saturating_mul(b as u64))
.saturating_add(T::DbWeight::get().reads(8 as u64))
.saturating_add(T::DbWeight::get().writes(8 as u64))
}
// Storage: Paras Heads (r:0 w:1)
fn set_current_head(b: u32, ) -> Weight {
Weight::from_ref_time(5_494_000 as u64)
// Standard Error: 0
.saturating_add(Weight::from_ref_time(1_000 as u64).saturating_mul(b as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
}