Newer
Older
const START_SESSION_INDEX: SessionIndex = 1;
run_to_session(START_SESSION_INDEX);
let para_id = LOWEST_PUBLIC_ID;
assert!(!Parachains::is_parathread(para_id));
let validation_code = test_validation_code(32);
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
assert_eq!(Balances::reserved_balance(&1), <Test as Config>::ParaDeposit::get());
validation_code.clone(),
conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
run_to_session(START_SESSION_INDEX + 2);
assert!(Parachains::is_parathread(para_id));
// Even though the registered validation code has a smaller size than the maximum the
// para manager's deposit is reserved as though they registered the maximum-sized code.
// Consequently, they can upgrade their code to the maximum size at any point without
// additional cost.
let validation_code_deposit =
max_code_size() as BalanceOf<Test> * <Test as Config>::DataDepositPerByte::get();
let head_deposit = 32 * <Test as Config>::DataDepositPerByte::get();
assert_eq!(
Balances::reserved_balance(&1),
<Test as Config>::ParaDeposit::get() + head_deposit + validation_code_deposit
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
#[test]
fn schedule_code_upgrade_validates_code() {
new_test_ext().execute_with(|| {
const START_SESSION_INDEX: SessionIndex = 1;
run_to_session(START_SESSION_INDEX);
let para_id = LOWEST_PUBLIC_ID;
assert!(!Parachains::is_parathread(para_id));
let validation_code = test_validation_code(32);
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
assert_eq!(Balances::reserved_balance(&1), <Test as Config>::ParaDeposit::get());
assert_ok!(Registrar::register(
RuntimeOrigin::signed(1),
para_id,
test_genesis_head(32),
validation_code.clone(),
));
conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
run_to_session(START_SESSION_INDEX + 2);
assert!(Parachains::is_parathread(para_id));
let new_code = test_validation_code(0);
assert_noop!(
Registrar::schedule_code_upgrade(
RuntimeOrigin::signed(1),
para_id,
new_code.clone(),
),
paras::Error::<Test>::InvalidCode
);
let new_code = test_validation_code(max_code_size() as usize + 1);
assert_noop!(
Registrar::schedule_code_upgrade(
RuntimeOrigin::signed(1),
para_id,
new_code.clone(),
),
paras::Error::<Test>::InvalidCode
);
});
}
#[test]
fn register_handles_basic_errors() {
new_test_ext().execute_with(|| {
let para_id = LOWEST_PUBLIC_ID;
para_id,
test_genesis_head(max_head_size() as usize),
test_validation_code(max_code_size() as usize),
),
Error::<Test>::NotReserved
);
// Successfully register para
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
para_id,
test_genesis_head(max_head_size() as usize),
test_validation_code(max_code_size() as usize),
),
Error::<Test>::NotOwner
);
assert_ok!(Registrar::register(
![Shawn Tabrizi Shawn Tabrizi's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Shawn Tabrizi
committed
test_genesis_head(max_head_size() as usize),
test_validation_code(max_code_size() as usize),
// Can skip pre-check and deregister para which's still onboarding.
assert_ok!(Registrar::deregister(RuntimeOrigin::root(), para_id));
// Can't do it again
para_id,
test_genesis_head(max_head_size() as usize),
test_validation_code(max_code_size() as usize),
),
Error::<Test>::NotReserved
);
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(2)));
para_id + 1,
test_genesis_head((max_head_size() + 1) as usize),
test_validation_code(max_code_size() as usize),
),
Error::<Test>::HeadDataTooLarge
);
para_id + 1,
test_genesis_head(max_head_size() as usize),
test_validation_code((max_code_size() + 1) as usize),
),
Error::<Test>::CodeTooLarge
);
// Needs enough funds for deposit
BalancesError::<Test, _>::InsufficientBalance
);
new_test_ext().execute_with(|| {
const START_SESSION_INDEX: SessionIndex = 1;
run_to_session(START_SESSION_INDEX);
let para_id = LOWEST_PUBLIC_ID;
assert!(!Parachains::is_parathread(para_id));
let validation_code = test_validation_code(32);
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
validation_code.clone(),
conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
run_to_session(START_SESSION_INDEX + 2);
assert!(Parachains::is_parathread(para_id));
assert_ok!(Registrar::deregister(RuntimeOrigin::root(), para_id,));
run_to_session(START_SESSION_INDEX + 4);
assert!(paras::Pallet::<Test>::lifecycle(para_id).is_none());
assert_eq!(Balances::reserved_balance(&1), 0);
});
}
#[test]
fn deregister_handles_basic_errors() {
new_test_ext().execute_with(|| {
const START_SESSION_INDEX: SessionIndex = 1;
run_to_session(START_SESSION_INDEX);
let para_id = LOWEST_PUBLIC_ID;
assert!(!Parachains::is_parathread(para_id));
let validation_code = test_validation_code(32);
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
validation_code.clone(),
conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
run_to_session(START_SESSION_INDEX + 2);
assert!(Parachains::is_parathread(para_id));
// Owner check
assert_noop!(Registrar::deregister(RuntimeOrigin::signed(2), para_id,), BadOrigin);
assert_ok!(Registrar::make_parachain(para_id));
run_to_session(START_SESSION_INDEX + 4);
// Cant directly deregister parachain
Registrar::deregister(RuntimeOrigin::root(), para_id,),
#[test]
fn swap_works() {
new_test_ext().execute_with(|| {
const START_SESSION_INDEX: SessionIndex = 1;
run_to_session(START_SESSION_INDEX);
// Successfully register first two parachains
let para_1 = LOWEST_PUBLIC_ID;
let para_2 = LOWEST_PUBLIC_ID + 1;
let validation_code = test_validation_code(max_code_size() as usize);
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
![Shawn Tabrizi Shawn Tabrizi's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Shawn Tabrizi
committed
test_genesis_head(max_head_size() as usize),
validation_code.clone(),
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(2)));
![Shawn Tabrizi Shawn Tabrizi's avatar](/assets/no_avatar-849f9c04a3a0d0cea2424ae97b27447dc64a7dbfae83c036c45b403392f0e8ba.png)
Shawn Tabrizi
committed
test_genesis_head(max_head_size() as usize),
validation_code.clone(),
conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
run_to_session(START_SESSION_INDEX + 2);
// Upgrade para 1 into a parachain
assert_ok!(Registrar::make_parachain(para_1));
// Set some mock swap data.
let mut swap_data = SwapData::get();
swap_data.insert(para_1, 69);
swap_data.insert(para_2, 1337);
SwapData::set(swap_data);
run_to_session(START_SESSION_INDEX + 4);
assert!(Parachains::is_parachain(para_1));
assert!(!Parachains::is_parathread(para_1));
assert!(!Parachains::is_parachain(para_2));
assert!(Parachains::is_parathread(para_2));
// Swap between parachain and parathread
assert_ok!(Registrar::swap(para_origin(para_1), para_1, para_2,));
assert_ok!(Registrar::swap(para_origin(para_2), para_2, para_1,));
System::assert_last_event(RuntimeEvent::Registrar(paras_registrar::Event::Swapped {
para_id: para_2,
other_id: para_1,
}));
run_to_session(START_SESSION_INDEX + 6);
assert!(!Parachains::is_parachain(para_1));
assert!(Parachains::is_parathread(para_1));
assert!(Parachains::is_parachain(para_2));
assert!(!Parachains::is_parathread(para_2));
// Data is swapped
assert_eq!(SwapData::get().get(¶_1).unwrap(), &1337);
assert_eq!(SwapData::get().get(¶_2).unwrap(), &69);
// Both paras initiate a swap
// Swap between parathread and parachain
assert_ok!(Registrar::swap(para_origin(para_1), para_1, para_2,));
assert_ok!(Registrar::swap(para_origin(para_2), para_2, para_1,));
System::assert_last_event(RuntimeEvent::Registrar(paras_registrar::Event::Swapped {
para_id: para_2,
other_id: para_1,
}));
// Data is swapped
assert_eq!(SwapData::get().get(¶_1).unwrap(), &69);
assert_eq!(SwapData::get().get(¶_2).unwrap(), &1337);
// Parachain to parachain swap
let para_3 = LOWEST_PUBLIC_ID + 2;
let validation_code = test_validation_code(max_code_size() as usize);
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(3)));
assert_ok!(Registrar::register(
RuntimeOrigin::signed(3),
para_3,
test_genesis_head(max_head_size() as usize),
validation_code.clone(),
conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX + 6);
run_to_session(START_SESSION_INDEX + 8);
// Upgrade para 3 into a parachain
assert_ok!(Registrar::make_parachain(para_3));
// Set some mock swap data.
let mut swap_data = SwapData::get();
swap_data.insert(para_3, 777);
SwapData::set(swap_data);
run_to_session(START_SESSION_INDEX + 10);
// Both are parachains
assert!(Parachains::is_parachain(para_3));
assert!(!Parachains::is_parathread(para_3));
assert!(Parachains::is_parachain(para_1));
assert!(!Parachains::is_parathread(para_1));
// Both paras initiate a swap
// Swap between parachain and parachain
assert_ok!(Registrar::swap(para_origin(para_1), para_1, para_3,));
assert_ok!(Registrar::swap(para_origin(para_3), para_3, para_1,));
System::assert_last_event(RuntimeEvent::Registrar(paras_registrar::Event::Swapped {
para_id: para_3,
other_id: para_1,
}));
// Data is swapped
assert_eq!(SwapData::get().get(¶_3).unwrap(), &69);
assert_eq!(SwapData::get().get(¶_1).unwrap(), &777);
#[test]
fn para_lock_works() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
let para_id = LOWEST_PUBLIC_ID;
assert_ok!(Registrar::register(
vec![1; 3].into(),
test_validation_code(32)
));
assert_noop!(Registrar::add_lock(RuntimeOrigin::signed(2), para_id), BadOrigin);
// 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
Registrar::ensure_root_para_or_owner(RuntimeOrigin::signed(1), para_id),
// 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));
// 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));
});
}
#[test]
fn swap_handles_bad_states() {
new_test_ext().execute_with(|| {
const START_SESSION_INDEX: SessionIndex = 1;
run_to_session(START_SESSION_INDEX);
let para_1 = LOWEST_PUBLIC_ID;
let para_2 = LOWEST_PUBLIC_ID + 1;
// paras are not yet registered
assert!(!Parachains::is_parathread(para_1));
assert!(!Parachains::is_parathread(para_2));
// Cannot even start a swap
assert_noop!(
Registrar::swap(RuntimeOrigin::root(), para_1, para_2),
Error::<Test>::NotRegistered
);
// We register Paras 1 and 2
let validation_code = test_validation_code(32);
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1)));
assert_ok!(Registrar::reserve(RuntimeOrigin::signed(2)));
para_1,
test_genesis_head(32),
validation_code.clone(),
));
assert_ok!(Registrar::register(
para_2,
test_genesis_head(32),
validation_code.clone(),
conclude_pvf_checking::<Test>(&validation_code, VALIDATORS, START_SESSION_INDEX);
assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
Error::<Test>::CannotSwap
);
run_to_session(START_SESSION_INDEX + 2);
// They are now parathreads (on-demand parachains).
assert!(Parachains::is_parathread(para_1));
assert!(Parachains::is_parathread(para_2));
// Cannot swap
assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
Error::<Test>::CannotSwap
);
// Some other external process will elevate one on-demand
// parachain to a lease holding parachain
assert_ok!(Registrar::make_parachain(para_1));
// Cannot swap
assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
Error::<Test>::CannotSwap
);
run_to_session(START_SESSION_INDEX + 3);
assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
Error::<Test>::CannotSwap
);
run_to_session(START_SESSION_INDEX + 4);
// It is now a lease holding parachain.
assert!(Parachains::is_parachain(para_1));
assert!(Parachains::is_parathread(para_2));
// Swap works here.
assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_2, para_1));
assert!(System::events().iter().any(|r| matches!(
r.event,
RuntimeEvent::Registrar(paras_registrar::Event::Swapped { .. })
)));
run_to_session(START_SESSION_INDEX + 5);
assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
Error::<Test>::CannotSwap
);
run_to_session(START_SESSION_INDEX + 6);
// Swap worked!
assert!(Parachains::is_parachain(para_2));
assert!(Parachains::is_parathread(para_1));
assert!(System::events().iter().any(|r| matches!(
r.event,
RuntimeEvent::Registrar(paras_registrar::Event::Swapped { .. })
)));
// Something starts to downgrade a para
assert_ok!(Registrar::make_parathread(para_2));
run_to_session(START_SESSION_INDEX + 7);
assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2));
Registrar::swap(RuntimeOrigin::root(), para_2, para_1),
Error::<Test>::CannotSwap
);
run_to_session(START_SESSION_INDEX + 8);
assert!(Parachains::is_parathread(para_1));
assert!(Parachains::is_parathread(para_2));
});
}
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking {
use super::{Pallet as Registrar, *};
use crate::traits::Registrar as RegistrarT;
use primitives::{MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MIN_CODE_SIZE};
use runtime_parachains::{paras, shared, Origin as ParaOrigin};
use frame_benchmarking::{account, benchmarks, whitelisted_caller};
fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
Shaun Wang
committed
let events = frame_system::Pallet::<T>::events();
let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
// compare to the last event record
let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
assert_eq!(event, &system_event);
}
fn register_para<T: Config>(id: u32) -> ParaId {
let para = ParaId::from(id);
let genesis_head = Registrar::<T>::worst_head_data();
let validation_code = Registrar::<T>::worst_validation_code();
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
assert_ok!(Registrar::<T>::reserve(RawOrigin::Signed(caller.clone()).into()));
assert_ok!(Registrar::<T>::register(
RawOrigin::Signed(caller).into(),
para,
genesis_head,
validation_code.clone()
));
assert_ok!(runtime_parachains::paras::Pallet::<T>::add_trusted_validation_code(
frame_system::Origin::<T>::Root.into(),
validation_code,
}
fn para_origin(id: u32) -> ParaOrigin {
ParaOrigin::Parachain(id.into())
}
// This function moves forward to the next scheduled session for parachain lifecycle upgrades.
fn next_scheduled_session<T: Config>() {
shared::Pallet::<T>::set_session_index(shared::Pallet::<T>::scheduled_session());
paras::Pallet::<T>::test_on_new_session();
where_clause { where ParaOrigin: Into<<T as frame_system::Config>::RuntimeOrigin> }
reserve {
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
}: _(RawOrigin::Signed(caller.clone()))
verify {
assert_last_event::<T>(Event::<T>::Reserved { para_id: LOWEST_PUBLIC_ID, who: caller }.into());
assert!(Paras::<T>::get(LOWEST_PUBLIC_ID).is_some());
assert_eq!(paras::Pallet::<T>::lifecycle(LOWEST_PUBLIC_ID), None);
let para = LOWEST_PUBLIC_ID;
let genesis_head = Registrar::<T>::worst_head_data();
let validation_code = Registrar::<T>::worst_validation_code();
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
assert_ok!(Registrar::<T>::reserve(RawOrigin::Signed(caller.clone()).into()));
}: _(RawOrigin::Signed(caller.clone()), para, genesis_head, validation_code.clone())
assert_last_event::<T>(Event::<T>::Registered{ para_id: para, manager: caller }.into());
assert_eq!(paras::Pallet::<T>::lifecycle(para), Some(ParaLifecycle::Onboarding));
assert_ok!(runtime_parachains::paras::Pallet::<T>::add_trusted_validation_code(
frame_system::Origin::<T>::Root.into(),
validation_code,
));
assert_eq!(paras::Pallet::<T>::lifecycle(para), Some(ParaLifecycle::Parathread));
force_register {
let manager: T::AccountId = account("manager", 0, 0);
let deposit = 0u32.into();
let para = ParaId::from(69);
let genesis_head = Registrar::<T>::worst_head_data();
let validation_code = Registrar::<T>::worst_validation_code();
}: _(RawOrigin::Root, manager.clone(), deposit, para, genesis_head, validation_code.clone())
assert_last_event::<T>(Event::<T>::Registered { para_id: para, manager }.into());
assert_eq!(paras::Pallet::<T>::lifecycle(para), Some(ParaLifecycle::Onboarding));
assert_ok!(runtime_parachains::paras::Pallet::<T>::add_trusted_validation_code(
frame_system::Origin::<T>::Root.into(),
validation_code,
));
next_scheduled_session::<T>();
assert_eq!(paras::Pallet::<T>::lifecycle(para), Some(ParaLifecycle::Parathread));
let para = register_para::<T>(LOWEST_PUBLIC_ID.into());
let caller: T::AccountId = whitelisted_caller();
}: _(RawOrigin::Signed(caller), para)
assert_last_event::<T>(Event::<T>::Deregistered { para_id: para }.into());
let parathread = register_para::<T>(LOWEST_PUBLIC_ID.into());
let parachain = register_para::<T>((LOWEST_PUBLIC_ID + 1).into());
let parachain_origin = para_origin(parachain.into());
// Actually finish registration process
next_scheduled_session::<T>();
// Upgrade the parachain
Registrar::<T>::make_parachain(parachain)?;
next_scheduled_session::<T>();
assert_eq!(paras::Pallet::<T>::lifecycle(parachain), Some(ParaLifecycle::Parachain));
assert_eq!(paras::Pallet::<T>::lifecycle(parathread), Some(ParaLifecycle::Parathread));
let caller: T::AccountId = whitelisted_caller();
Registrar::<T>::swap(parachain_origin.into(), parachain, parathread)?;
}: _(RawOrigin::Signed(caller.clone()), parathread, parachain)
verify {
next_scheduled_session::<T>();
// Swapped!
assert_eq!(paras::Pallet::<T>::lifecycle(parachain), Some(ParaLifecycle::Parathread));
assert_eq!(paras::Pallet::<T>::lifecycle(parathread), Some(ParaLifecycle::Parachain));
let b in MIN_CODE_SIZE .. 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(),
crate::integration_tests::Test,
);
}