Unverified Commit a21cd64e authored by Shawn Tabrizi's avatar Shawn Tabrizi Committed by GitHub
Browse files

Parachain Onboarding Extras (#2713)

* clear_lease

* schedule upgrade and downgrade

* fix compile

* comments
parent 9164205e
Pipeline #130880 failed with stages
in 28 minutes and 48 seconds
......@@ -25,7 +25,9 @@ use frame_support::{
};
use frame_system::ensure_root;
use runtime_parachains::{
configuration, dmp, ump, hrmp, paras::{self, ParaGenesisArgs},
configuration, dmp, ump, hrmp,
ParaLifecycle,
paras::{self, ParaGenesisArgs},
};
use primitives::v1::Id as ParaId;
use parity_scale_codec::Encode;
......@@ -49,6 +51,14 @@ decl_error! {
DefinitelyNotWasm,
/// Could not schedule para cleanup.
CouldntCleanup,
/// Not a parathread.
NotParathread,
/// Not a parachain.
NotParachain,
/// Cannot upgrade parathread.
CannotUpgrade,
/// Cannot downgrade parachain.
CannotDowngrade,
}
}
......@@ -78,6 +88,26 @@ decl_module! {
Ok(())
}
/// Upgrade a parathread to a parachain
#[weight = (1_000, DispatchClass::Operational)]
pub fn sudo_schedule_parathread_upgrade(origin, id: ParaId) -> DispatchResult {
ensure_root(origin)?;
// Para backend should think this is a parathread...
ensure!(paras::Module::<T>::lifecycle(id) == Some(ParaLifecycle::Parathread), Error::<T>::NotParathread);
runtime_parachains::schedule_parathread_upgrade::<T>(id).map_err(|_| Error::<T>::CannotUpgrade)?;
Ok(())
}
/// Downgrade a parachain to a parathread
#[weight = (1_000, DispatchClass::Operational)]
pub fn sudo_schedule_parachain_downgrade(origin, id: ParaId) -> DispatchResult {
ensure_root(origin)?;
// Para backend should think this is a parachain...
ensure!(paras::Module::<T>::lifecycle(id) == Some(ParaLifecycle::Parachain), Error::<T>::NotParachain);
runtime_parachains::schedule_parachain_downgrade::<T>(id).map_err(|_| Error::<T>::CannotDowngrade)?;
Ok(())
}
/// Send a downward XCM to the given para.
///
/// The given parachain should exist and the payload should not exceed the preconfigured size
......
......@@ -154,6 +154,8 @@ decl_module! {
/// Just a hotwire into the `lease_out` call, in case Root wants to force some lease to happen
/// independently of any other on-chain mechanism to use it.
///
/// Can only be called by the Root origin.
#[weight = T::WeightInfo::force_lease()]
fn force_lease(origin,
para: ParaId,
......@@ -167,6 +169,26 @@ decl_module! {
.map_err(|_| Error::<T>::LeaseError)?;
Ok(())
}
/// Clear all leases for a Para Id, refunding any deposits back to the original owners.
///
/// Can only be called by the Root origin.
#[weight = 0] // TODO: Benchmarks
fn clear_all_leases(origin,
para: ParaId,
) -> DispatchResult {
ensure_root(origin)?;
let deposits = Self::all_deposits_held(para);
// Refund any deposits for these leases
for (who, deposit) in deposits {
let err_amount = T::Currency::unreserve(&who, deposit);
debug_assert!(err_amount.is_zero());
}
Leases::<T>::remove(para);
Ok(())
}
}
}
......@@ -216,7 +238,7 @@ impl<T: Config> Module<T> {
// If this is less than what we were holding for this leaser's now-ended lease, then
// unreserve it.
if let Some(rebate) = ended_lease.1.checked_sub(&now_held) {
T::Currency::unreserve( &ended_lease.0, rebate);
T::Currency::unreserve(&ended_lease.0, rebate);
}
}
......@@ -249,6 +271,34 @@ impl<T: Config> Module<T> {
parachains.len() as u32,
)
}
// Return a vector of (user, balance) for all deposits for a parachain.
// Useful when trying to clean up a parachain leases, as this would tell
// you all the balances you need to unreserve.
fn all_deposits_held(para: ParaId) -> Vec<(T::AccountId, BalanceOf<T>)> {
let mut tracker = sp_std::collections::btree_map::BTreeMap::new();
Leases::<T>::get(para)
.into_iter()
.for_each(|lease| {
match lease {
Some((who, amount)) => {
match tracker.get(&who) {
Some(prev_amount) => {
if amount > *prev_amount {
tracker.insert(who, amount);
}
},
None => {
tracker.insert(who, amount);
}
}
},
None => {},
}
});
tracker.into_iter().collect()
}
}
impl<T: Config> crate::traits::OnSwap for Module<T> {
......@@ -653,6 +703,37 @@ mod tests {
]);
});
}
#[test]
fn clear_all_leases_works() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_ok!(TestRegistrar::<Test>::register(1, ParaId::from(1), Default::default(), Default::default()));
let max_num = 5u32;
// max_num different people are reserved for leases to Para ID 1
for i in 1u32 ..= max_num {
let j: u64 = i.into();
assert_ok!(Slots::lease_out(1.into(), &j, j * 10, i * i, i));
assert_eq!(Slots::deposit_held(1.into(), &j), j * 10);
assert_eq!(Balances::reserved_balance(j), j * 10);
}
assert_ok!(Slots::clear_all_leases(Origin::root(), 1.into()));
// Balances cleaned up correctly
for i in 1u32 ..= max_num {
let j: u64 = i.into();
assert_eq!(Slots::deposit_held(1.into(), &j), 0);
assert_eq!(Balances::reserved_balance(j), 0);
}
// Leases is empty.
assert!(Leases::<Test>::get(ParaId::from(1)).is_empty());
});
}
}
#[cfg(feature = "runtime-benchmarks")]
......
......@@ -78,6 +78,7 @@ pub trait Registrar {
}
/// Error type for something that went wrong with leasing.
#[derive(Debug)]
pub enum LeaseError {
/// Unable to reserve the funds in the leaser's account.
ReserveFailed,
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment