inclusion.rs 50.4 KiB
Newer Older
			<PendingAvailability<Test>>::insert(chain_a, CandidatePendingAvailability {
				core: CoreIndex::from(0),
				descriptor: candidate_a.descriptor,
				availability_votes: default_availability_votes(),
				relay_parent_number: 0,
				backed_in_number: 0,
			});
			PendingAvailabilityCommitments::insert(chain_a, candidate_a.commitments);

			let candidate_b = TestCandidateBuilder {
				para_id: chain_b,
				head_data: vec![5, 6, 7, 8].into(),
				..Default::default()
			}.build();

			<PendingAvailability<Test>>::insert(chain_b, CandidatePendingAvailability {
				core: CoreIndex::from(1),
				descriptor: candidate_b.descriptor,
				availability_votes: default_availability_votes(),
				relay_parent_number: 0,
				backed_in_number: 0,
			});
			PendingAvailabilityCommitments::insert(chain_b, candidate_b.commitments);

			// this bitfield signals that a and b are available.
			let a_and_b_available = {
				let mut bare_bitfield = default_bitfield();
				*bare_bitfield.0.get_mut(0).unwrap() = true;
				*bare_bitfield.0.get_mut(1).unwrap() = true;

				bare_bitfield
			};

			// this bitfield signals that only a is available.
			let a_available = {
				let mut bare_bitfield = default_bitfield();
				*bare_bitfield.0.get_mut(0).unwrap() = true;

				bare_bitfield
			};

			let threshold = availability_threshold(validators.len());

			// 4 of 5 first value >= 2/3
			assert_eq!(threshold, 4);

			let signed_bitfields = validators.iter().enumerate().filter_map(|(i, key)| {
				let to_sign = if i < 3 {
					a_and_b_available.clone()
				} else if i < 4 {
					a_available.clone()
				} else {
					// sign nothing.
					return None
				};

				Some(sign_bitfield(
					key,
					i as ValidatorIndex,
					to_sign,
					&signing_context,
				))
			}).collect();

			assert!(Inclusion::process_bitfields(
				&core_lookup,
			).is_ok());

			// chain A had 4 signing off, which is >= threshold.
			// chain B has 3 signing off, which is < threshold.
			assert!(<PendingAvailability<Test>>::get(&chain_a).is_none());
			assert!(<PendingAvailabilityCommitments>::get(&chain_a).is_none());
			assert!(<PendingAvailabilityCommitments>::get(&chain_b).is_some());
			assert_eq!(
				<PendingAvailability<Test>>::get(&chain_b).unwrap().availability_votes,
				{
					// check that votes from first 3 were tracked.

					let mut votes = default_availability_votes();
					*votes.get_mut(0).unwrap() = true;
					*votes.get_mut(1).unwrap() = true;
					*votes.get_mut(2).unwrap() = true;

					votes
				},
			);

			// and check that chain head was enacted.
			assert_eq!(Paras::para_head(&chain_a), Some(vec![1, 2, 3, 4].into()));
		});
	}

	#[test]
	fn candidate_checks() {
		let chain_a = ParaId::from(1);
		let chain_b = ParaId::from(2);
		let thread_a = ParaId::from(3);

		let paras = vec![(chain_a, true), (chain_b, true), (thread_a, false)];
		let validators = vec![
			Sr25519Keyring::Alice,
			Sr25519Keyring::Bob,
			Sr25519Keyring::Charlie,
			Sr25519Keyring::Dave,
			Sr25519Keyring::Ferdie,
		];
		let validator_public = validator_pubkeys(&validators);

		new_test_ext(genesis_config(paras)).execute_with(|| {
			Validators::set(validator_public.clone());
			CurrentSessionIndex::set(5);

			run_to_block(5, |_| None);

			let signing_context = SigningContext {
				parent_hash: System::parent_hash(),
				session_index: 5,
			};

			let group_validators = |group_index: GroupIndex| match group_index {
				group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1]),
				group_index if group_index == GroupIndex::from(1) => Some(vec![2, 3]),
				group_index if group_index == GroupIndex::from(2) => Some(vec![4]),
				_ => panic!("Group index out of bounds for 2 parachains and 1 parathread core"),
			};

			let thread_collator: CollatorId = Sr25519Keyring::Two.public().into();

			let chain_a_assignment = CoreAssignment {
				core: CoreIndex::from(0),
				kind: AssignmentKind::Parachain,
				group_idx: GroupIndex::from(0),
			};

			let chain_b_assignment = CoreAssignment {
				core: CoreIndex::from(1),
				para_id: chain_b,
				kind: AssignmentKind::Parachain,
				group_idx: GroupIndex::from(1),
			};

			let thread_a_assignment = CoreAssignment {
				core: CoreIndex::from(2),
				kind: AssignmentKind::Parathread(thread_collator.clone(), 0),
				group_idx: GroupIndex::from(2),
			};

			// unscheduled candidate.
			{
				let mut candidate = TestCandidateBuilder {
					para_id: chain_a,
					relay_parent: System::parent_hash(),
					pov_hash: Hash::from([1; 32]),
					..Default::default()
				}.build();
				collator_sign_candidate(
					Sr25519Keyring::One,
					&mut candidate,
				);

				let backed = back_candidate(
					candidate,
					&validators,
					group_validators(GroupIndex::from(0)).unwrap().as_ref(),
					&signing_context,
					BackingKind::Threshold,
				);

				assert!(Inclusion::process_candidates(
					vec![backed],
					vec![chain_b_assignment.clone()],
					&group_validators,
				).is_err());
			}

			// candidates out of order.
			{
				let mut candidate_a = TestCandidateBuilder {
					para_id: chain_a,
					relay_parent: System::parent_hash(),
					pov_hash: Hash::from([1; 32]),
					..Default::default()
				}.build();
				let mut candidate_b = TestCandidateBuilder {
					para_id: chain_b,
					relay_parent: System::parent_hash(),
					pov_hash: Hash::from([2; 32]),
					..Default::default()
				}.build();

				collator_sign_candidate(
					Sr25519Keyring::One,
					&mut candidate_a,
				);

				collator_sign_candidate(
					Sr25519Keyring::Two,
					&mut candidate_b,
				);

				let backed_a = back_candidate(
					candidate_a,
					&validators,
					group_validators(GroupIndex::from(0)).unwrap().as_ref(),
					&signing_context,
					BackingKind::Threshold,
				);

				let backed_b = back_candidate(
					candidate_b,
					&validators,
					group_validators(GroupIndex::from(1)).unwrap().as_ref(),
					&signing_context,
					BackingKind::Threshold,
				);

				assert!(Inclusion::process_candidates(
					vec![backed_b, backed_a],
					vec![chain_a_assignment.clone(), chain_b_assignment.clone()],
					&group_validators,
				).is_err());
			}

			// candidate not backed.
			{
				let mut candidate = TestCandidateBuilder {
					para_id: chain_a,
					relay_parent: System::parent_hash(),
					pov_hash: Hash::from([1; 32]),
					..Default::default()
				}.build();
				collator_sign_candidate(
					Sr25519Keyring::One,
					&mut candidate,
				);

				let backed = back_candidate(
					candidate,
					&validators,
					group_validators(GroupIndex::from(0)).unwrap().as_ref(),
					&signing_context,
					BackingKind::Lacking,
				);

				assert!(Inclusion::process_candidates(
					vec![backed],
					vec![chain_a_assignment.clone()],
					&group_validators,
				).is_err());
			}

			// candidate not in parent context.
			{
				let wrong_parent_hash = Hash::from([222; 32]);
				assert!(System::parent_hash() != wrong_parent_hash);

				let mut candidate = TestCandidateBuilder {
					para_id: chain_a,
					relay_parent: wrong_parent_hash,
					pov_hash: Hash::from([1; 32]),
					..Default::default()
				}.build();
				collator_sign_candidate(
					Sr25519Keyring::One,
					&mut candidate,
				);

				let backed = back_candidate(
					candidate,
					&validators,
					group_validators(GroupIndex::from(0)).unwrap().as_ref(),
					&signing_context,
					BackingKind::Threshold,
				);

				assert!(Inclusion::process_candidates(
					vec![backed],
					vec![chain_a_assignment.clone()],
					&group_validators,
				).is_err());
			}

			// candidate has wrong collator.
			{
				let mut candidate = TestCandidateBuilder {
					para_id: thread_a,
					relay_parent: System::parent_hash(),
					pov_hash: Hash::from([1; 32]),
					..Default::default()
				}.build();

				assert!(CollatorId::from(Sr25519Keyring::One.public()) != thread_collator);
				collator_sign_candidate(
					Sr25519Keyring::One,
					&mut candidate,
				);

				let backed = back_candidate(
					candidate,
					&validators,
					group_validators(GroupIndex::from(2)).unwrap().as_ref(),
					&signing_context,
					BackingKind::Threshold,
				);

				assert!(Inclusion::process_candidates(
					vec![backed],
					vec![
						chain_a_assignment.clone(),
						chain_b_assignment.clone(),
						thread_a_assignment.clone(),
					],
					&group_validators,
				).is_err());
			}

			// candidate not well-signed by collator.
			{
				let mut candidate = TestCandidateBuilder {
					para_id: thread_a,
					relay_parent: System::parent_hash(),
					pov_hash: Hash::from([1; 32]),
					..Default::default()
				}.build();

				assert_eq!(CollatorId::from(Sr25519Keyring::Two.public()), thread_collator);
				collator_sign_candidate(
					Sr25519Keyring::Two,
					&mut candidate,
				);

				// change the candidate after signing.
				candidate.descriptor.pov_hash = Hash::from([2; 32]);

				let backed = back_candidate(
					candidate,
					&validators,
					group_validators(GroupIndex::from(2)).unwrap().as_ref(),
					&signing_context,
					BackingKind::Threshold,
				);

				assert!(Inclusion::process_candidates(
					vec![backed],
					vec![thread_a_assignment.clone()],
					&group_validators,
				).is_err());
			}

			// para occupied - reject.
			{
				let mut candidate = TestCandidateBuilder {
					para_id: chain_a,
					relay_parent: System::parent_hash(),
					pov_hash: Hash::from([1; 32]),
					..Default::default()
				}.build();

				collator_sign_candidate(
					Sr25519Keyring::One,
					&mut candidate,
				);

				let backed = back_candidate(
					candidate,
					&validators,
					group_validators(GroupIndex::from(0)).unwrap().as_ref(),
					&signing_context,
					BackingKind::Threshold,
				);

				let candidate = TestCandidateBuilder::default().build();
				<PendingAvailability<Test>>::insert(&chain_a, CandidatePendingAvailability {
					core: CoreIndex::from(0),
					descriptor: candidate.descriptor,
					availability_votes: default_availability_votes(),
					relay_parent_number: 3,
					backed_in_number: 4,
				});
				<PendingAvailabilityCommitments>::insert(&chain_a, candidate.commitments);

				assert!(Inclusion::process_candidates(
					vec![backed],
					vec![chain_a_assignment.clone()],
					&group_validators,
				).is_err());

				<PendingAvailability<Test>>::remove(&chain_a);
				<PendingAvailabilityCommitments>::remove(&chain_a);
			}

			// messed up commitments storage - do not panic - reject.
			{
				let mut candidate = TestCandidateBuilder {
					para_id: chain_a,
					relay_parent: System::parent_hash(),
					pov_hash: Hash::from([1; 32]),
					..Default::default()
				}.build();

				collator_sign_candidate(
					Sr25519Keyring::One,
					&mut candidate,
				);

				// this is not supposed to happen
				<PendingAvailabilityCommitments>::insert(&chain_a, candidate.commitments.clone());

				let backed = back_candidate(
					candidate,
					&validators,
					group_validators(GroupIndex::from(0)).unwrap().as_ref(),
					&signing_context,
					BackingKind::Threshold,
				);

				assert!(Inclusion::process_candidates(
					vec![backed],
					vec![chain_a_assignment.clone()],
					&group_validators,
				).is_err());

				<PendingAvailabilityCommitments>::remove(&chain_a);
			}

			// interfering code upgrade - reject
			{
				let mut candidate = TestCandidateBuilder {
					para_id: chain_a,
					relay_parent: System::parent_hash(),
					pov_hash: Hash::from([1; 32]),
					new_validation_code: Some(vec![5, 6, 7, 8].into()),
					..Default::default()
				}.build();

				collator_sign_candidate(
					Sr25519Keyring::One,
					&mut candidate,
				);

				let backed = back_candidate(
					candidate,
					&validators,
					group_validators(GroupIndex::from(0)).unwrap().as_ref(),
					&signing_context,
					BackingKind::Threshold,
				);

				Paras::schedule_code_upgrade(
					chain_a,
					vec![1, 2, 3, 4].into(),
					10,
				);

				assert_eq!(Paras::last_code_upgrade(chain_a, true), Some(10));

				assert!(Inclusion::process_candidates(
					vec![backed],
					vec![thread_a_assignment.clone()],
					&group_validators,
				).is_err());
			}
		});
	}

	#[test]
	fn backing_works() {
		let chain_a = ParaId::from(1);
		let chain_b = ParaId::from(2);
		let thread_a = ParaId::from(3);

		let paras = vec![(chain_a, true), (chain_b, true), (thread_a, false)];
		let validators = vec![
			Sr25519Keyring::Alice,
			Sr25519Keyring::Bob,
			Sr25519Keyring::Charlie,
			Sr25519Keyring::Dave,
			Sr25519Keyring::Ferdie,
		];
		let validator_public = validator_pubkeys(&validators);

		new_test_ext(genesis_config(paras)).execute_with(|| {
			Validators::set(validator_public.clone());
			CurrentSessionIndex::set(5);

			run_to_block(5, |_| None);

			let signing_context = SigningContext {
				parent_hash: System::parent_hash(),
				session_index: 5,
			};

			let group_validators = |group_index: GroupIndex| match group_index {
				group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1]),
				group_index if group_index == GroupIndex::from(1) => Some(vec![2, 3]),
				group_index if group_index == GroupIndex::from(2) => Some(vec![4]),
				_ => panic!("Group index out of bounds for 2 parachains and 1 parathread core"),
			};

			let thread_collator: CollatorId = Sr25519Keyring::Two.public().into();

			let chain_a_assignment = CoreAssignment {
				core: CoreIndex::from(0),
				para_id: chain_a,
				kind: AssignmentKind::Parachain,
				group_idx: GroupIndex::from(0),
			};

			let chain_b_assignment = CoreAssignment {
				core: CoreIndex::from(1),
				para_id: chain_b,
				kind: AssignmentKind::Parachain,
				group_idx: GroupIndex::from(1),
			};

			let thread_a_assignment = CoreAssignment {
				core: CoreIndex::from(2),
				para_id: thread_a,
				kind: AssignmentKind::Parathread(thread_collator.clone(), 0),
				group_idx: GroupIndex::from(2),
			};

			let mut candidate_a = TestCandidateBuilder {
				para_id: chain_a,
				relay_parent: System::parent_hash(),
				pov_hash: Hash::from([1; 32]),
				..Default::default()
			}.build();
			collator_sign_candidate(
				Sr25519Keyring::One,
				&mut candidate_a,
			);

			let mut candidate_b = TestCandidateBuilder {
				para_id: chain_b,
				relay_parent: System::parent_hash(),
				pov_hash: Hash::from([2; 32]),
				..Default::default()
			}.build();
			collator_sign_candidate(
				Sr25519Keyring::One,
				&mut candidate_b,
			);

			let mut candidate_c = TestCandidateBuilder {
				para_id: thread_a,
				relay_parent: System::parent_hash(),
				pov_hash: Hash::from([3; 32]),
				..Default::default()
			}.build();
			collator_sign_candidate(
				Sr25519Keyring::Two,
				&mut candidate_c,
			);

			let backed_a = back_candidate(
				candidate_a.clone(),
				&validators,
				group_validators(GroupIndex::from(0)).unwrap().as_ref(),
				&signing_context,
				BackingKind::Threshold,
			);

			let backed_b = back_candidate(
				candidate_b.clone(),
				&validators,
				group_validators(GroupIndex::from(1)).unwrap().as_ref(),
				&signing_context,
				BackingKind::Threshold,
			);

			let backed_c = back_candidate(
				candidate_c.clone(),
				&validators,
				group_validators(GroupIndex::from(2)).unwrap().as_ref(),
				&signing_context,
				BackingKind::Threshold,
			);

			let occupied_cores = Inclusion::process_candidates(
				vec![backed_a, backed_b, backed_c],
				vec![
					chain_a_assignment.clone(),
					chain_b_assignment.clone(),
					thread_a_assignment.clone(),
				],
				&group_validators,
			).expect("candidates scheduled, in order, and backed");

			assert_eq!(occupied_cores, vec![CoreIndex::from(0), CoreIndex::from(1), CoreIndex::from(2)]);

			assert_eq!(
				<PendingAvailability<Test>>::get(&chain_a),
				Some(CandidatePendingAvailability {
					core: CoreIndex::from(0),
					descriptor: candidate_a.descriptor,
					availability_votes: default_availability_votes(),
					relay_parent_number: System::block_number() - 1,
					backed_in_number: System::block_number(),
				})
			);
			assert_eq!(
				<PendingAvailabilityCommitments>::get(&chain_a),
				Some(candidate_a.commitments),
			);

			assert_eq!(
				<PendingAvailability<Test>>::get(&chain_b),
				Some(CandidatePendingAvailability {
					core: CoreIndex::from(1),
					descriptor: candidate_b.descriptor,
					availability_votes: default_availability_votes(),
					relay_parent_number: System::block_number() - 1,
					backed_in_number: System::block_number(),
				})
			);
			assert_eq!(
				<PendingAvailabilityCommitments>::get(&chain_b),
				Some(candidate_b.commitments),
			);

			assert_eq!(
				<PendingAvailability<Test>>::get(&thread_a),
				Some(CandidatePendingAvailability {
					core: CoreIndex::from(2),
					descriptor: candidate_c.descriptor,
					availability_votes: default_availability_votes(),
					relay_parent_number: System::block_number() - 1,
					backed_in_number: System::block_number(),
				})
			);
			assert_eq!(
				<PendingAvailabilityCommitments>::get(&thread_a),
				Some(candidate_c.commitments),
			);
		});
	}

	#[test]
	fn session_change_wipes_and_updates_session_info() {
		let chain_a = ParaId::from(1);
		let chain_b = ParaId::from(2);
		let thread_a = ParaId::from(3);

		let paras = vec![(chain_a, true), (chain_b, true), (thread_a, false)];
		let validators = vec![
			Sr25519Keyring::Alice,
			Sr25519Keyring::Bob,
			Sr25519Keyring::Charlie,
			Sr25519Keyring::Dave,
			Sr25519Keyring::Ferdie,
		];
		let validator_public = validator_pubkeys(&validators);

		new_test_ext(genesis_config(paras)).execute_with(|| {
			Validators::set(validator_public.clone());
			CurrentSessionIndex::set(5);

			let validators_new = vec![
				Sr25519Keyring::Alice,
				Sr25519Keyring::Bob,
				Sr25519Keyring::Charlie,
			];

			let validator_public_new = validator_pubkeys(&validators_new);

			run_to_block(10, |_| None);

			<AvailabilityBitfields<Test>>::insert(
				&0,
				AvailabilityBitfieldRecord {
					bitfield: default_bitfield(),
					submitted_at: 9,
				},
			);

			<AvailabilityBitfields<Test>>::insert(
				&1,
				AvailabilityBitfieldRecord {
					bitfield: default_bitfield(),
					submitted_at: 9,
				},
			);

			<AvailabilityBitfields<Test>>::insert(
				&4,
				AvailabilityBitfieldRecord {
					bitfield: default_bitfield(),
					submitted_at: 9,
				},
			);

			let candidate = TestCandidateBuilder::default().build();
			<PendingAvailability<Test>>::insert(&chain_a, CandidatePendingAvailability {
				core: CoreIndex::from(0),
				descriptor: candidate.descriptor.clone(),
				availability_votes: default_availability_votes(),
				relay_parent_number: 5,
				backed_in_number: 6,
			});
			<PendingAvailabilityCommitments>::insert(&chain_a, candidate.commitments.clone());

			<PendingAvailability<Test>>::insert(&chain_b, CandidatePendingAvailability {
				core: CoreIndex::from(1),
				descriptor: candidate.descriptor,
				availability_votes: default_availability_votes(),
				relay_parent_number: 6,
				backed_in_number: 7,
			});
			<PendingAvailabilityCommitments>::insert(&chain_b, candidate.commitments);

			run_to_block(11, |_| None);

			assert_eq!(Validators::get(), validator_public);
			assert_eq!(CurrentSessionIndex::get(), 5);

			assert!(<AvailabilityBitfields<Test>>::get(&0).is_some());
			assert!(<AvailabilityBitfields<Test>>::get(&1).is_some());
			assert!(<AvailabilityBitfields<Test>>::get(&4).is_some());

			assert!(<PendingAvailability<Test>>::get(&chain_a).is_some());
			assert!(<PendingAvailability<Test>>::get(&chain_b).is_some());
			assert!(<PendingAvailabilityCommitments>::get(&chain_a).is_some());
			assert!(<PendingAvailabilityCommitments>::get(&chain_b).is_some());

			run_to_block(12, |n| match n {
				12 => Some(SessionChangeNotification {
					validators: validator_public_new.clone(),
					queued: Vec::new(),
					prev_config: default_config(),
					new_config: default_config(),
					random_seed: Default::default(),
					session_index: 6,
				}),
				_ => None,
			});

			assert_eq!(Validators::get(), validator_public_new);
			assert_eq!(CurrentSessionIndex::get(), 6);

			assert!(<AvailabilityBitfields<Test>>::get(&0).is_none());
			assert!(<AvailabilityBitfields<Test>>::get(&1).is_none());
			assert!(<AvailabilityBitfields<Test>>::get(&4).is_none());

			assert!(<PendingAvailability<Test>>::get(&chain_a).is_none());
			assert!(<PendingAvailability<Test>>::get(&chain_b).is_none());
			assert!(<PendingAvailabilityCommitments>::get(&chain_a).is_none());
			assert!(<PendingAvailabilityCommitments>::get(&chain_b).is_none());

			assert!(<AvailabilityBitfields<Test>>::iter().collect::<Vec<_>>().is_empty());
			assert!(<PendingAvailability<Test>>::iter().collect::<Vec<_>>().is_empty());
			assert!(<PendingAvailabilityCommitments>::iter().collect::<Vec<_>>().is_empty());