registrar.rs 48.6 KiB
Newer Older

	#[test]
	fn genesis_registration_works() {
		let parachains = vec![
			(5u32.into(), vec![1,2,3], vec![1]),
			(100u32.into(), vec![4,5,6], vec![2,]),
		];

		new_test_ext(parachains).execute_with(|| {
			// Need to trigger on_initialize
			run_to_block(2);
			// Genesis registration works
			assert_eq!(Registrar::active_paras(), vec![(5u32.into(), None), (100u32.into(), None)]);
			assert_eq!(
				Registrar::paras(&ParaId::from(5u32)),
				Some(ParaInfo { scheduling: Scheduling::Always }),
			);
			assert_eq!(
				Registrar::paras(&ParaId::from(100u32)),
				Some(ParaInfo { scheduling: Scheduling::Always }),
			);
			assert_eq!(Parachains::parachain_code(&ParaId::from(5u32)), Some(vec![1, 2, 3]));
			assert_eq!(Parachains::parachain_code(&ParaId::from(100u32)), Some(vec![4, 5, 6]));
		});
	}

	#[test]
	fn swap_chain_and_thread_works() {
		new_test_ext(vec![]).execute_with(|| {
			assert_ok!(Registrar::set_thread_count(Origin::ROOT, 1));

			// Need to trigger on_initialize
			run_to_block(2);

			// Register a new parathread
			assert_ok!(Registrar::register_parathread(
				Origin::signed(1u64),
				vec![1; 3],
				vec![1; 3],
			));

			// Lease out a new parachain
			assert_ok!(Slots::new_auction(Origin::ROOT, 5, 1));
			assert_ok!(Slots::bid(Origin::signed(1), 0, 1, 1, 4, 1));

			run_to_block(9);
			// Ensure that the thread is scheduled around the swap time.
			let col = Sr25519Keyring::One.public().into();
			schedule_thread(user_id(0), &[1; 3], &col);

			run_to_block(10);
			let h = BlakeTwo256::hash(&[2u8; 3]);
			assert_ok!(Slots::fix_deploy_data(Origin::signed(1), 0, user_id(1), h, 3, vec![2; 3]));
			assert_ok!(Slots::elaborate_deploy_data(Origin::signed(0), user_id(1), vec![2; 3]));
			assert_ok!(Slots::set_offboarding(Origin::signed(user_id(1).into_account()), 1));

			run_to_block(11);
			// should be one active parachain and one active parathread.
			assert_eq!(Registrar::active_paras(), vec![
				(user_id(0), Some((col.clone(), Retriable::WithRetries(0)))),
				(user_id(1), None),
			]);

			// One half of the swap call does not actually trigger the swap.
			assert_ok!(Registrar::swap(parachains::Origin::Parachain(user_id(0)).into(), user_id(1)));

			// Nothing changes from what was originally registered
			assert_eq!(Registrar::paras(&user_id(0)), Some(ParaInfo { scheduling: Scheduling::Dynamic }));
			assert_eq!(Registrar::paras(&user_id(1)), Some(ParaInfo { scheduling: Scheduling::Always }));
			assert_eq!(super::Parachains::get(), vec![user_id(1)]);
			assert_eq!(Slots::managed_ids(), vec![user_id(1)]);
			assert_eq!(Slots::deposits(user_id(1)), vec![1; 3]);
			assert_eq!(Slots::offboarding(user_id(1)), 1);
			assert_eq!(Parachains::parachain_code(&user_id(0)), Some(vec![1u8; 3]));
			assert_eq!(Parachains::parachain_head(&user_id(0)), Some(vec![1u8; 3]));
			assert_eq!(Parachains::parachain_code(&user_id(1)), Some(vec![2u8; 3]));
			assert_eq!(Parachains::parachain_head(&user_id(1)), Some(vec![2u8; 3]));
			// Intention to swap is added
			assert_eq!(PendingSwap::get(user_id(0)), Some(user_id(1)));

			// Intention to swap is reciprocated, swap actually happens
			assert_ok!(Registrar::swap(parachains::Origin::Parachain(user_id(1)).into(), user_id(0)));

			assert_eq!(Registrar::paras(&user_id(0)), Some(ParaInfo { scheduling: Scheduling::Always }));
			assert_eq!(Registrar::paras(&user_id(1)), Some(ParaInfo { scheduling: Scheduling::Dynamic }));
			assert_eq!(super::Parachains::get(), vec![user_id(0)]);
			assert_eq!(Slots::managed_ids(), vec![user_id(0)]);
			assert_eq!(Slots::deposits(user_id(0)), vec![1; 3]);
			assert_eq!(Slots::offboarding(user_id(0)), 1);
			assert_eq!(Parachains::parachain_code(&user_id(0)), Some(vec![1u8; 3]));
			assert_eq!(Parachains::parachain_head(&user_id(0)), Some(vec![1u8; 3]));
			assert_eq!(Parachains::parachain_code(&user_id(1)), Some(vec![2u8; 3]));
			assert_eq!(Parachains::parachain_head(&user_id(1)), Some(vec![2u8; 3]));

			// Intention to swap is no longer present
			assert_eq!(PendingSwap::get(user_id(0)), None);
			assert_eq!(PendingSwap::get(user_id(1)), None);

			run_to_block(12);
			// thread should not be queued or scheduled any more, even though it would otherwise be
			// being retried..
			assert_eq!(Registrar::active_paras(), vec![(user_id(0), None)]);
		});
	}

	#[test]
	fn swap_handles_funds_correctly() {
		new_test_ext(vec![]).execute_with(|| {
			assert_ok!(Registrar::set_thread_count(Origin::ROOT, 1));

			// Need to trigger on_initialize
			run_to_block(2);

			let initial_1_balance = Balances::free_balance(1);
			let initial_2_balance = Balances::free_balance(2);

			// User 1 register a new parathread
			assert_ok!(Registrar::register_parathread(
				Origin::signed(1),
				vec![1; 3],
				vec![1; 3],
			));

			// User 2 leases out a new parachain
			assert_ok!(Slots::new_auction(Origin::ROOT, 5, 1));
			assert_ok!(Slots::bid(Origin::signed(2), 0, 1, 1, 4, 1));

			run_to_block(9);

			// Swap the parachain and parathread
			assert_ok!(Registrar::swap(parachains::Origin::Parachain(user_id(0)).into(), user_id(1)));
			assert_ok!(Registrar::swap(parachains::Origin::Parachain(user_id(1)).into(), user_id(0)));

			// Deregister the parathread that was originally a parachain
			assert_ok!(Registrar::deregister_parathread(parachains::Origin::Parachain(user_id(1)).into()));

			// Go past when a parachain loses its slot
			run_to_block(50);

			// Funds are correctly returned
			assert_eq!(Balances::free_balance(1), initial_1_balance);
			assert_eq!(Balances::free_balance(2), initial_2_balance);
		});
	}

	#[test]
	fn register_deregister_chains_works() {
		let parachains = vec![
			(1u32.into(), vec![1; 3], vec![1; 3]),
		];

		new_test_ext(parachains).execute_with(|| {
			// Need to trigger on_initialize
			run_to_block(2);

			// Genesis registration works
			assert_eq!(Registrar::active_paras(), vec![(1u32.into(), None)]);
			assert_eq!(
				Registrar::paras(&ParaId::from(1u32)),
				Some(ParaInfo { scheduling: Scheduling::Always })
			);
			assert_eq!(Parachains::parachain_code(&ParaId::from(1u32)), Some(vec![1; 3]));

			// Register a new parachain
			assert_ok!(Registrar::register_para(
				Origin::ROOT,
				2u32.into(),
				ParaInfo { scheduling: Scheduling::Always },
				vec![2; 3],
				vec![2; 3],
			));

			let orig_bal = Balances::free_balance(&3u64);
			// Register a new parathread
			assert_ok!(Registrar::register_parathread(
				Origin::signed(3u64),
				vec![3; 3],
				vec![3; 3],
			));
			// deposit should be taken (reserved)
Gavin Wood's avatar
Gavin Wood committed
			assert_eq!(Balances::free_balance(3u64) + ParathreadDeposit::get(), orig_bal);
			assert_eq!(Balances::reserved_balance(3u64), ParathreadDeposit::get());

			run_to_block(3);

			// New paras are registered
			assert_eq!(Registrar::active_paras(), vec![(1u32.into(), None), (2u32.into(), None)]);
			assert_eq!(
				Registrar::paras(&ParaId::from(2u32)),
				Some(ParaInfo { scheduling: Scheduling::Always })
			);
			assert_eq!(
				Registrar::paras(&user_id(0)),
				Some(ParaInfo { scheduling: Scheduling::Dynamic })
			);
			assert_eq!(Parachains::parachain_code(&ParaId::from(2u32)), Some(vec![2; 3]));
			assert_eq!(Parachains::parachain_code(&user_id(0)), Some(vec![3; 3]));

			assert_ok!(Registrar::deregister_para(Origin::ROOT, 2u32.into()));
			assert_ok!(Registrar::deregister_parathread(
				parachains::Origin::Parachain(user_id(0)).into()
			));
			// reserved balance should be returned.
Gavin Wood's avatar
Gavin Wood committed
			assert_eq!(Balances::free_balance(3u64), orig_bal);
			assert_eq!(Balances::reserved_balance(3u64), 0);

			run_to_block(4);

			assert_eq!(Registrar::active_paras(), vec![(1u32.into(), None)]);
			assert_eq!(Registrar::paras(&ParaId::from(2u32)), None);
			assert_eq!(Parachains::parachain_code(&ParaId::from(2u32)), None);
			assert_eq!(Registrar::paras(&user_id(0)), None);
			assert_eq!(Parachains::parachain_code(&user_id(0)), None);
		});
	}

	#[test]
	fn parathread_scheduling_works() {
		new_test_ext(vec![]).execute_with(|| {
			assert_ok!(Registrar::set_thread_count(Origin::ROOT, 1));

			run_to_block(2);

			// Register a new parathread
			assert_ok!(Registrar::register_parathread(
				Origin::signed(3u64),
				vec![3; 3],
				vec![3; 3],
			));

			run_to_block(3);

			// transaction submitted to get parathread progressed.
			let col = Sr25519Keyring::One.public().into();
			schedule_thread(user_id(0), &[3; 3], &col);

			run_to_block(5);
			assert_eq!(Registrar::active_paras(), vec![
				(user_id(0), Some((col.clone(), Retriable::WithRetries(0))))
			]);
			assert_ok!(Parachains::set_heads(Origin::NONE, vec![
				attest(user_id(0), &Sr25519Keyring::One.pair().into(), &[3; 3], &[0; 0])
			]));

			run_to_block(6);
			// at next block, it shouldn't be retried.
			assert_eq!(Registrar::active_paras(), vec![]);
		});
	}

	#[test]
	fn removing_scheduled_parathread_works() {
		new_test_ext(vec![]).execute_with(|| {
			assert_ok!(Registrar::set_thread_count(Origin::ROOT, 1));

			run_to_block(2);

			// Register some parathreads.
			assert_ok!(Registrar::register_parathread(Origin::signed(3), vec![3; 3], vec![3; 3]));

			run_to_block(3);
			// transaction submitted to get parathread progressed.
			let col = Sr25519Keyring::One.public().into();
			schedule_thread(user_id(0), &[3; 3], &col);

			// now we remove the parathread
			assert_ok!(Registrar::deregister_parathread(
				parachains::Origin::Parachain(user_id(0)).into()
			));

			run_to_block(5);
			assert_eq!(Registrar::active_paras(), vec![]);  // should not be scheduled.

			assert_ok!(Registrar::register_parathread(Origin::signed(3), vec![4; 3], vec![4; 3]));

			run_to_block(6);
			// transaction submitted to get parathread progressed.
			schedule_thread(user_id(1), &[4; 3], &col);

			run_to_block(9);
			// thread's slot was missed and is now being re-scheduled.

			assert_ok!(Registrar::deregister_parathread(
				parachains::Origin::Parachain(user_id(1)).into()
			));

			run_to_block(10);
			// thread's rescheduled slot was missed, but should not be reschedule since it was
			// removed.
			assert_eq!(Registrar::active_paras(), vec![]);  // should not be scheduled.
		});
	}

	#[test]
	fn parathread_rescheduling_works() {
		new_test_ext(vec![]).execute_with(|| {
			assert_ok!(Registrar::set_thread_count(Origin::ROOT, 1));

			run_to_block(2);

			// Register some parathreads.
			assert_ok!(Registrar::register_parathread(Origin::signed(3), vec![3; 3], vec![3; 3]));
			assert_ok!(Registrar::register_parathread(Origin::signed(4), vec![4; 3], vec![4; 3]));
			assert_ok!(Registrar::register_parathread(Origin::signed(5), vec![5; 3], vec![5; 3]));

			run_to_block(3);

			// transaction submitted to get parathread progressed.
			let col = Sr25519Keyring::One.public().into();
			schedule_thread(user_id(0), &[3; 3], &col);

			// 4x: the initial time it was scheduled, plus 3 retries.
			for n in 5..9 {
				run_to_block(n);
				assert_eq!(Registrar::active_paras(), vec![
					(user_id(0), Some((col.clone(), Retriable::WithRetries((n - 5) as u32))))
				]);
			}

			// missed too many times. dropped.
			run_to_block(9);
			assert_eq!(Registrar::active_paras(), vec![]);

			// schedule and miss all 3 and check that they go through the queueing system ok.
			assert_ok!(Registrar::set_thread_count(Origin::ROOT, 2));
			schedule_thread(user_id(0), &[3; 3], &col);
			schedule_thread(user_id(1), &[4; 3], &col);

			run_to_block(10);
			schedule_thread(user_id(2), &[5; 3], &col);

			// 0 and 1 scheduled as normal.
			run_to_block(11);
			assert_eq!(Registrar::active_paras(), vec![
				(user_id(0), Some((col.clone(), Retriable::WithRetries(0)))),
				(user_id(1), Some((col.clone(), Retriable::WithRetries(0))))
			]);

			// 2 scheduled, 0 retried
			run_to_block(12);
			assert_eq!(Registrar::active_paras(), vec![
				(user_id(0), Some((col.clone(), Retriable::WithRetries(1)))),
				(user_id(2), Some((col.clone(), Retriable::WithRetries(0)))),
			]);

			// 1 retried
			run_to_block(13);
			assert_eq!(Registrar::active_paras(), vec![
				(user_id(1), Some((col.clone(), Retriable::WithRetries(1))))
			]);

			// 2 retried
			run_to_block(14);
			assert_eq!(Registrar::active_paras(), vec![
				(user_id(2), Some((col.clone(), Retriable::WithRetries(1))))
			]);

			run_to_block(15);
			assert_eq!(Registrar::active_paras(), vec![
				(user_id(0), Some((col.clone(), Retriable::WithRetries(2))))
			]);

			run_to_block(16);
			assert_eq!(Registrar::active_paras(), vec![
				(user_id(1), Some((col.clone(), Retriable::WithRetries(2))))
			]);

			run_to_block(17);
			assert_eq!(Registrar::active_paras(), vec![
				(user_id(2), Some((col.clone(), Retriable::WithRetries(2))))
			]);
		});
	}

	#[test]
	fn parathread_auction_handles_basic_errors() {
		new_test_ext(vec![]).execute_with(|| {
			run_to_block(2);
			let o = Origin::signed(0);
			assert_ok!(Registrar::register_parathread(o, vec![7, 8, 9], vec![1, 1, 1]));

			run_to_block(3);
			assert_eq!(
				Registrar::paras(&user_id(0)),
				Some(ParaInfo { scheduling: Scheduling::Dynamic })
			);

			let good_para_id = user_id(0);
			let bad_para_id = user_id(1);
			let bad_head_hash = <Test as system::Trait>::Hashing::hash(&vec![1, 2, 1]);
			let good_head_hash = <Test as system::Trait>::Hashing::hash(&vec![1, 1, 1]);
			let info = DispatchInfo::default();

			// Allow for threads
			assert_ok!(Registrar::set_thread_count(Origin::ROOT, 10));

			// Bad parathread id
			let col = CollatorId::default();
			let inner = super::Call::select_parathread(bad_para_id, col.clone(), good_head_hash);
			let call = Call::Registrar(inner);
			assert!(
				LimitParathreadCommits::<Test>(std::marker::PhantomData)
					.validate(&0, &call, info, 0).is_err()
			);

			// Bad head data
			let inner = super::Call::select_parathread(good_para_id, col.clone(), bad_head_hash);
			let call = Call::Registrar(inner);
			assert!(
				LimitParathreadCommits::<Test>(std::marker::PhantomData)
					.validate(&0, &call, info, 0).is_err()
			);

			// No duplicates
			let inner = super::Call::select_parathread(good_para_id, col.clone(), good_head_hash);
			let call = Call::Registrar(inner);
			assert!(
				LimitParathreadCommits::<Test>(std::marker::PhantomData)
					.validate(&0, &call, info, 0).is_ok()
			);
			assert!(
				LimitParathreadCommits::<Test>(std::marker::PhantomData)
					.validate(&0, &call, info, 0).is_err()
			);
		});
	}

	#[test]
	fn parathread_auction_works() {
		new_test_ext(vec![]).execute_with(|| {
			run_to_block(2);
			// Register 5 parathreads
			for x in 0..5 {
				let o = Origin::signed(x as u64);
				assert_ok!(Registrar::register_parathread(o, vec![x; 3], vec![x; 3]));
			}

			run_to_block(3);

			for x in 0..5 {
				assert_eq!(
					Registrar::paras(&user_id(x)),
					Some(ParaInfo { scheduling: Scheduling::Dynamic })
				);
			}

			// Only 3 slots available... who will win??
			assert_ok!(Registrar::set_thread_count(Origin::ROOT, 3));

			// Everyone wants a thread
			for x in 0..5 {
				let para_id = user_id(x as u32);
				let collator_id = CollatorId::default();
				let head_hash = <Test as system::Trait>::Hashing::hash(&vec![x; 3]);
				let inner = super::Call::select_parathread(para_id, collator_id, head_hash);
				let call = Call::Registrar(inner);
				let info = DispatchInfo::default();

				// First 3 transactions win a slot
				if x < 3 {
					assert!(
						LimitParathreadCommits::<Test>(std::marker::PhantomData)
							.validate(&0, &call, info, 0)
							.is_ok()
					);
				} else {
					// All others lose
					assert_noop!(
						LimitParathreadCommits::<Test>(std::marker::PhantomData)
							.validate(&0, &call, info, 0),
						InvalidTransaction::ExhaustsResources,
					);
				}
			}

			// 3 Threads are selected
			assert_eq!(
				SelectedThreads::get()[1],
				vec![
					(user_id(0), CollatorId::default()),
					(user_id(1), CollatorId::default()),
					(user_id(2), CollatorId::default()),
				]
			);

			// Assuming Queue Size is 2
			assert_eq!(<Test as self::Trait>::QueueSize::get(), 2);

			// 2 blocks later
			run_to_block(5);
			// Threads left queue
			assert_eq!(SelectedThreads::get()[0], vec![]);
			// Threads are active
			assert_eq!(
				Registrar::active_paras(),
				vec![
					(user_id(0), Some((CollatorId::default(), Retriable::WithRetries(0)))),
					(user_id(1), Some((CollatorId::default(), Retriable::WithRetries(0)))),
					(user_id(2), Some((CollatorId::default(), Retriable::WithRetries(0)))),
				]
			);
		});
	}

	#[test]
	fn register_does_not_enforce_limits_when_registering() {
		new_test_ext(vec![]).execute_with(|| {
			let bad_code_size = <Test as parachains::Trait>::MaxCodeSize::get() + 1;
			let bad_head_size = <Test as parachains::Trait>::MaxHeadDataSize::get() + 1;

			let code = vec![1u8; bad_code_size as _];
			let head_data = vec![2u8; bad_head_size as _];

			assert!(!<Registrar as super::Registrar<u64>>::code_size_allowed(bad_code_size));
			assert!(!<Registrar as super::Registrar<u64>>::head_data_size_allowed(bad_head_size));

			let id = <Registrar as super::Registrar<u64>>::new_id();
			assert_ok!(<Registrar as super::Registrar<u64>>::register_para(
				id,
				ParaInfo { scheduling: Scheduling::Always },
				code,
				head_data,
			));
		});
	}