use crate::{
extension::{AsPerson, AsPersonInfo},
mock::*,
pallet::{AccountToPersonalId, Origin as PeopleOrigin},
WeightInfo, *,
};
use frame_support::{
assert_noop, assert_ok,
dispatch::Pays,
traits::{Get, OnIdle, OnPoll},
weights::RuntimeDbWeight,
BoundedVec,
};
use sp_runtime::{
transaction_validity::InvalidTransaction::{self, BadSigner},
DispatchResult, Weight,
};
use verifiable::demo_impls::Simple;
fn generate_people_with_index(
start: u8,
end: u8,
) -> Vec<(PersonalId, MemberOf<Test>, SecretOf<Test>)> {
let mut people = Vec::new();
for i in start..=end {
let person = PeoplePallet::reserve_new_id();
let secret = Simple::new_secret([i; 32]);
let public = Simple::member_from_secret(&secret);
PeoplePallet::recognize_personhood(person, Some(public)).unwrap();
people.push((person, public, secret));
}
people
}
fn suspended_indices_list(ring_index: u32) -> BoundedVec<u32, <Test as Config>::MaxRingSize> {
let suspended_indices = PendingSuspensions::<Test>::get(ring_index);
assert!(&suspended_indices[..].windows(2).all(|pair| pair[0] < pair[1]));
suspended_indices
}
fn build_ring(ring_index: RingIndex, limit: Option<u32>) -> DispatchResult {
let to_include = PeoplePallet::should_build_ring(
ring_index,
limit.unwrap_or(<Test as Config>::MaxRingSize::get()),
)
.ok_or(Error::<Test>::StillFresh)?;
PeoplePallet::build_ring(ring_index, to_include)
}
#[test]
fn build_ring_works() {
TestExt::new().execute_with(|| {
PeoplePallet::set_onboarding_size(RuntimeOrigin::root(), 5).unwrap();
assert_noop!(build_ring(RI_ZERO, None), Error::<Test>::StillFresh);
generate_people_with_index(0, 3);
assert_eq!(Keys::<Test>::count(), 4);
assert_noop!(build_ring(RI_ZERO, None), Error::<Test>::StillFresh);
assert_noop!(PeoplePallet::onboard_people(), Error::<Test>::Incomplete);
generate_people_with_index(4, 4);
assert_ok!(PeoplePallet::onboard_people());
assert!(!Root::<Test>::contains_key(0));
assert_eq!(RingKeysStatus::<Test>::get(0), RingStatus { total: 5, included: 0 });
assert_ok!(build_ring(RI_ZERO, None));
assert!(Root::<Test>::contains_key(0));
generate_people_with_index(5, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
generate_people_with_index(10, 35);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(1, None));
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(2, None));
assert_noop!(PeoplePallet::onboard_people(), Error::<Test>::Incomplete);
});
}
#[test]
fn recognize_person_with_duplicate_key() {
TestExt::new().execute_with(|| {
let person_a = PeoplePallet::reserve_new_id();
let secret_a = Simple::new_secret([1; 32]);
let key_a = Simple::member_from_secret(&secret_a);
PeoplePallet::recognize_personhood(person_a, Some(key_a)).unwrap();
let person_b = PeoplePallet::reserve_new_id();
assert_noop!(
PeoplePallet::recognize_personhood(person_b, Some(key_a)),
Error::<Test>::KeyAlreadyInUse
);
});
}
#[test]
fn recognize_same_person_2_times() {
TestExt::new().execute_with(|| {
let person_a = PeoplePallet::reserve_new_id();
let secret_a = Simple::new_secret([1; 32]);
let key_a = Simple::member_from_secret(&secret_a);
assert_ok!(PeoplePallet::recognize_personhood(person_a, Some(key_a)));
assert!(People::<Test>::get(person_a).is_some());
assert_noop!(
PeoplePallet::recognize_personhood(person_a, Some(key_a)),
Error::<Test>::KeyAlreadyInUse,
);
assert_noop!(
PeoplePallet::renew_id_reservation(person_a),
Error::<Test>::PersonalIdReservationCannotRenew,
);
});
}
#[test]
fn recognize_person_with_duplicate_key_after_suspend() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
OnboardingSize::<Test>::set(1);
let person_a = PeoplePallet::reserve_new_id();
let person_b = PeoplePallet::reserve_new_id();
let person_c = PeoplePallet::reserve_new_id();
let secret_a = Simple::new_secret([1; 32]);
let secret_b = Simple::new_secret([2; 32]);
let secret_c = Simple::new_secret([3; 32]);
let key_a = Simple::member_from_secret(&secret_a);
let key_b = Simple::member_from_secret(&secret_b);
let key_c = Simple::member_from_secret(&secret_c);
assert_ok!(PeoplePallet::recognize_personhood(person_a, Some(key_a)));
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(PeoplePallet::recognize_personhood(person_b, Some(key_b)));
assert_eq!(
People::<Test>::get(person_a).unwrap().position,
RingPosition::Included {
ring_index: 0,
ring_position: 0,
scheduled_for_removal: false
}
);
assert_eq!(
People::<Test>::get(person_b).unwrap().position,
RingPosition::Onboarding { queue_page: 0 }
);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert_ok!(PeoplePallet::suspend_personhood(&[person_a, person_b]));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert_eq!(People::<Test>::get(person_a).unwrap().position, RingPosition::Suspended);
assert_eq!(People::<Test>::get(person_b).unwrap().position, RingPosition::Suspended);
assert_noop!(
PeoplePallet::recognize_personhood(person_c, Some(key_a)),
Error::<Test>::KeyAlreadyInUse
);
assert_ok!(PeoplePallet::recognize_personhood(person_c, Some(key_c)));
assert_ok!(PeoplePallet::recognize_personhood(person_a, None));
assert_ok!(PeoplePallet::recognize_personhood(person_b, None));
assert_eq!(Keys::<Test>::get(key_a), Some(person_a));
assert_eq!(Keys::<Test>::get(key_b), Some(person_b));
});
}
#[test]
fn id_reservation_works() {
TestExt::new().execute_with(|| {
assert_eq!(NextPersonalId::<Test>::get(), 0);
assert!(!ReservedPersonalId::<Test>::contains_key(0));
assert!(People::<Test>::get(0).is_none());
assert_noop!(
PeoplePallet::renew_id_reservation(0),
Error::<Test>::PersonalIdReservationCannotRenew,
);
assert_eq!(PeoplePallet::reserve_new_id(), 0);
assert_eq!(NextPersonalId::<Test>::get(), 1);
assert!(ReservedPersonalId::<Test>::contains_key(0));
assert!(People::<Test>::get(0).is_none());
assert_noop!(
PeoplePallet::renew_id_reservation(0),
Error::<Test>::PersonalIdReservationCannotRenew,
);
assert_eq!(PeoplePallet::reserve_new_id(), 1);
assert_eq!(NextPersonalId::<Test>::get(), 2);
assert!(ReservedPersonalId::<Test>::contains_key(1));
assert!(People::<Test>::get(1).is_none());
assert_ok!(PeoplePallet::cancel_id_reservation(0));
assert!(!ReservedPersonalId::<Test>::contains_key(0));
assert!(People::<Test>::get(0).is_none());
assert_eq!(PeoplePallet::reserve_new_id(), 2);
assert_eq!(NextPersonalId::<Test>::get(), 3);
assert!(ReservedPersonalId::<Test>::contains_key(2));
assert!(People::<Test>::get(2).is_none());
assert_ok!(PeoplePallet::renew_id_reservation(0));
assert!(ReservedPersonalId::<Test>::contains_key(0));
assert!(People::<Test>::get(0).is_none());
assert_noop!(
PeoplePallet::renew_id_reservation(0),
Error::<Test>::PersonalIdReservationCannotRenew,
);
assert_ok!(PeoplePallet::recognize_personhood(0, Some([0; 32])));
assert!(People::<Test>::get(0).is_some());
assert!(!ReservedPersonalId::<Test>::contains_key(0));
assert_noop!(
PeoplePallet::renew_id_reservation(0),
Error::<Test>::PersonalIdReservationCannotRenew,
);
});
}
#[test]
fn force_recognize_personhood_works() {
TestExt::new().execute_with(|| {
use verifiable::demo_impls::Simple;
let num_people = 5;
let mut keys = Vec::new();
for i in 0..num_people {
let secret = Simple::new_secret([i as u8; 32]);
let public_key = Simple::member_from_secret(&secret);
keys.push(public_key);
}
for id in 0..num_people {
assert!(People::<Test>::get(id).is_none());
assert!(!ReservedPersonalId::<Test>::contains_key(id));
}
assert_eq!(NextPersonalId::<Test>::get(), 0);
assert_ok!(PeoplePallet::force_recognize_personhood(RuntimeOrigin::root(), keys.clone()));
for (i, key) in keys.clone().into_iter().enumerate() {
let who = i as PersonalId;
let record = People::<Test>::get(who).expect("Person should be recognized");
assert_eq!(record.key, key);
assert!(!ReservedPersonalId::<Test>::contains_key(who));
}
assert_eq!(NextPersonalId::<Test>::get(), num_people);
assert!(People::<Test>::get(num_people).is_none());
assert_noop!(
PeoplePallet::force_recognize_personhood(RuntimeOrigin::signed(0), keys.clone()),
sp_runtime::DispatchError::BadOrigin
);
let another_key = {
let secret = Simple::new_secret([233; 32]);
Simple::member_from_secret(&secret)
};
assert_noop!(
PeoplePallet::force_recognize_personhood(
RuntimeOrigin::root(),
vec![another_key, another_key]
),
Error::<Test>::KeyAlreadyInUse
);
});
}
#[test]
fn cannot_renew_future_id() {
TestExt::new().execute_with(|| {
assert_eq!(NextPersonalId::<Test>::get(), 0);
assert_noop!(
PeoplePallet::renew_id_reservation(0),
Error::<Test>::PersonalIdReservationCannotRenew
);
assert_noop!(
PeoplePallet::renew_id_reservation(1),
Error::<Test>::PersonalIdReservationCannotRenew
);
let first_id = PeoplePallet::reserve_new_id();
assert_eq!(first_id, 0);
assert_eq!(NextPersonalId::<Test>::get(), 1);
assert_noop!(
PeoplePallet::renew_id_reservation(0),
Error::<Test>::PersonalIdReservationCannotRenew
);
assert_noop!(
PeoplePallet::renew_id_reservation(1),
Error::<Test>::PersonalIdReservationCannotRenew
);
assert_ok!(PeoplePallet::cancel_id_reservation(0));
assert_ok!(PeoplePallet::renew_id_reservation(0));
assert_noop!(
PeoplePallet::renew_id_reservation(1),
Error::<Test>::PersonalIdReservationCannotRenew
);
});
}
#[test]
fn test_set_personal_id_account() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 3);
assert!(AccountToPersonalId::<Test>::get(42).is_none());
assert!(People::<Test>::get(1).unwrap().account.is_none());
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(1));
assert_ok!(PeoplePallet::set_personal_id_account(origin, 42, 0), Pays::No.into());
assert_eq!(AccountToPersonalId::<Test>::get(42), Some(1));
assert_eq!(People::<Test>::get(1).unwrap().account, Some(42));
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(1));
assert_ok!(PeoplePallet::set_personal_id_account(origin, 43, 0), Pays::Yes.into());
assert!(AccountToPersonalId::<Test>::get(42).is_none());
assert_eq!(AccountToPersonalId::<Test>::get(43), Some(1));
assert_eq!(People::<Test>::get(1).unwrap().account, Some(43));
let origin = RuntimeOrigin::signed(44);
assert_noop!(
PeoplePallet::set_personal_id_account(origin, 44, 0),
sp_runtime::DispatchError::BadOrigin
);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(2));
assert_ok!(PeoplePallet::set_personal_id_account(origin, 45, 0), Pays::No.into());
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_noop!(
PeoplePallet::set_personal_id_account(origin, 45, 0),
Error::<Test>::AccountInUse
);
});
}
#[test]
fn test_unset_personal_id_account() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 1);
let id_origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(1));
assert_ok!(
PeoplePallet::set_personal_id_account(id_origin.clone(), 50, 0),
Pays::No.into()
);
assert_eq!(AccountToPersonalId::<Test>::get(50), Some(1));
assert_ok!(PeoplePallet::unset_personal_id_account(id_origin.clone()), Pays::Yes.into());
assert!(AccountToPersonalId::<Test>::get(50).is_none());
assert!(People::<Test>::get(1).unwrap().account.is_none());
assert_noop!(
PeoplePallet::unset_personal_id_account(id_origin.clone()),
Error::<Test>::InvalidAccount
);
});
}
#[test]
fn test_as_personal_identity_with_account_check_and_nonce() {
new_test_ext().execute_with(|| {
let dummy_call = frame_system::Call::<Test>::remark { remark: vec![] };
let account: u64 = 42;
let nonce: u64 = 0;
let tx_ext = (
AsPerson::<Test>::new(Some(AsPersonInfo::AsPersonalIdentityWithAccount(nonce))),
frame_system::CheckNonce::<Test>::from(nonce),
);
assert_noop!(
exec_tx(Some(account), tx_ext, dummy_call.clone()),
InvalidTransaction::BadSigner
);
let personal_id = generate_people_with_index(0, 0).pop().unwrap().0;
AccountToPersonalId::<Test>::insert(account, personal_id);
System::inc_sufficients(&account);
let nonce: u64 = 0;
let tx_ext = (
AsPerson::<Test>::new(Some(AsPersonInfo::AsPersonalIdentityWithAccount(nonce))),
frame_system::CheckNonce::<Test>::from(nonce),
);
assert_ok!(exec_tx(Some(account), tx_ext, dummy_call.clone()));
assert_eq!(frame_system::Pallet::<Test>::account_nonce(account), 1);
let nonce: u64 = 1;
let tx_ext = (
AsPerson::<Test>::new(Some(AsPersonInfo::AsPersonalIdentityWithAccount(nonce))),
frame_system::CheckNonce::<Test>::from(nonce),
);
assert_ok!(exec_tx(Some(account), tx_ext, dummy_call.clone()));
assert_eq!(frame_system::Pallet::<Test>::account_nonce(account), 2);
let nonce: u64 = 1;
let tx_ext = (
AsPerson::<Test>::new(Some(AsPersonInfo::AsPersonalIdentityWithAccount(nonce))),
frame_system::CheckNonce::<Test>::from(nonce),
);
assert_noop!(exec_tx(Some(account), tx_ext, dummy_call), InvalidTransaction::Stale);
assert_eq!(frame_system::Pallet::<Test>::account_nonce(account), 2);
});
}
mod on_idle {
use super::*;
use frame_support::assert_storage_noop;
#[test]
fn one_ring_and_no_suspensions() {
TestExt::new().execute_with(|| {
let mut ext = new_test_ext();
ext.execute_with(|| {
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_storage_noop!(PeoplePallet::on_idle(0, Weight::MAX));
});
});
}
#[test]
fn no_rings_and_empty_queue() {
TestExt::new().execute_with(|| {
let mut ext = new_test_ext();
ext.execute_with(|| {
assert_storage_noop!(PeoplePallet::on_idle(0, Weight::MAX));
});
});
}
#[test]
fn no_rings_and_not_enough_people_in_the_queue() {
TestExt::new().execute_with(|| {
let mut ext = new_test_ext();
ext.execute_with(|| {
generate_people_with_index(0, 3);
assert_storage_noop!(PeoplePallet::on_idle(0, Weight::MAX));
});
});
}
#[test]
fn no_rings_and_some_people_awaiting_onboarding() {
TestExt::new().execute_with(|| {
let mut ext = new_test_ext();
ext.execute_with(|| {
generate_people_with_index(0, 3);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert_storage_noop!(PeoplePallet::on_idle(0, Weight::MAX));
});
});
}
mod suspensions {
use super::*;
#[test]
fn one_ring_with_suspensions() {
TestExt::new().execute_with(|| {
let mut ext = new_test_ext();
ext.execute_with(|| {
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[1];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
let mut meter = WeightMeter::new();
PeoplePallet::on_poll(0, &mut meter);
assert!(RingsState::<Test>::get().append_only());
PeoplePallet::on_idle(0, Weight::MAX);
assert_eq!(RingKeys::<Test>::get(RI_ZERO).len(), 9);
assert_eq!(
RingKeysStatus::<Test>::get(RI_ZERO),
RingStatus { total: 9, included: 9 }
);
});
});
}
#[test]
fn multiple_rings_with_suspensions() {
TestExt::new().execute_with(|| {
let mut ext = new_test_ext();
ext.execute_with(|| {
generate_people_with_index(0, 19);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(build_ring(1, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[1, 11];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
let mut meter = WeightMeter::new();
PeoplePallet::on_poll(0, &mut meter);
assert!(RingsState::<Test>::get().append_only());
PeoplePallet::on_idle(0, Weight::MAX);
assert!(PeoplePallet::should_build_ring(
RI_ZERO,
<Test as Config>::MaxRingSize::get()
)
.is_none());
assert_eq!(
RingKeysStatus::<Test>::get(RI_ZERO),
RingStatus { total: 9, included: 9 }
);
assert!(PeoplePallet::should_build_ring(
1,
<Test as Config>::MaxRingSize::get()
)
.is_none());
assert_eq!(
RingKeysStatus::<Test>::get(1),
RingStatus { total: 9, included: 9 }
);
});
});
}
}
mod onboarding {
use super::*;
#[test]
fn onboarding_all_conditions_met() {
TestExt::new().execute_with(|| {
let mut ext = new_test_ext();
ext.execute_with(|| {
PeoplePallet::set_onboarding_size(RuntimeOrigin::root(), 3).unwrap();
OnboardingSize::<Test>::set(3);
generate_people_with_index(0, 3);
assert_storage_noop!(PeoplePallet::on_idle(
0,
<Test as Config>::WeightInfo::on_idle_base(),
));
assert_storage_noop!(PeoplePallet::on_idle(
0,
<Test as Config>::WeightInfo::on_idle_base().saturating_mul(2),
));
let expected_weight = <Test as Config>::WeightInfo::on_idle_base()
.saturating_add(<Test as Config>::WeightInfo::onboard_people());
assert_eq!(
PeoplePallet::on_idle(
0,
<Test as Config>::WeightInfo::on_idle_base()
.saturating_add(<Test as Config>::WeightInfo::onboard_people())
.saturating_mul(2),
),
expected_weight
);
assert_eq!(
RingKeysStatus::<Test>::get(RI_ZERO),
RingStatus { total: 4, included: 0 }
);
});
});
}
}
}
mod manual_tasks {
use super::*;
use frame_support::BoundedVec;
#[test]
fn works_for_build_ring() {
TestExt::new().execute_with(|| {
OnboardingSize::<Test>::set(1);
let member_key = Simple::member_from_secret(&Simple::new_secret([0u8; 32]));
let keys: BoundedVec<MemberOf<Test>, <Test as Config>::MaxRingSize> =
BoundedVec::try_from(vec![member_key]).expect("failed to init members");
let ring_status = RingStatus { total: keys.len().saturated_into(), included: 0 };
RingKeys::<Test>::insert(RI_ZERO, keys);
RingKeysStatus::<Test>::insert(RI_ZERO, ring_status);
assert_ok!(PeoplePallet::build_ring_manual(
RuntimeOrigin::signed(0),
0,
Some(OnboardingSize::<Test>::get())
));
});
}
#[test]
fn works_for_onboard_people() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 19);
assert_ok!(PeoplePallet::onboard_people_manual(RuntimeOrigin::signed(0)));
});
}
#[test]
fn checks_the_need_to_build_the_ring() {
TestExt::new().execute_with(|| {
OnboardingSize::<Test>::set(5);
assert!(PeoplePallet::should_build_ring(0, OnboardingSize::<Test>::get()).is_none());
generate_people_with_index(0, 3);
assert!(PeoplePallet::should_build_ring(0, OnboardingSize::<Test>::get()).is_none());
generate_people_with_index(4, 4);
assert_ok!(PeoplePallet::onboard_people());
assert_eq!(PeoplePallet::should_build_ring(0, OnboardingSize::<Test>::get()), Some(5));
});
}
}
mod chunks {
use super::*;
use frame_support::traits::Get;
use sp_runtime::BoundedVec;
#[test]
#[should_panic]
fn no_chunks_first_page() {
new_test_ext().execute_with(|| {
Chunks::<Test>::remove(0);
assert!(PeoplePallet::fetch_chunks(0..5).is_err());
})
}
#[test]
#[should_panic]
fn no_chunks_second_page() {
new_test_ext().execute_with(|| {
let chunk_page_size: u32 = <Test as Config>::ChunkPageSize::get();
Chunks::<Test>::remove(1);
assert!(PeoplePallet::fetch_chunks(
(chunk_page_size - 1) as usize..(chunk_page_size * 2) as usize
)
.is_err());
})
}
#[test]
#[should_panic]
fn insufficient_chunks() {
new_test_ext().execute_with(|| {
let drain = Chunks::<Test>::drain();
for _ in drain {}
let chunk_page_size: u32 = <Test as Config>::ChunkPageSize::get();
let chunks: BoundedVec<(), _> = [(); 4096]
.into_iter()
.take(chunk_page_size as usize - 1)
.collect::<Vec<_>>()
.try_into()
.unwrap();
Chunks::<Test>::insert(0, chunks);
assert!(PeoplePallet::fetch_chunks(0..(chunk_page_size as usize + 1)).is_err());
})
}
#[test]
fn single_page_chunks() {
new_test_ext().execute_with(|| {
let chunk_page_size: u32 = <Test as Config>::ChunkPageSize::get();
assert_eq!(
PeoplePallet::fetch_chunks(0..(chunk_page_size - 1) as usize).unwrap().len(),
chunk_page_size as usize - 1
);
assert_eq!(
PeoplePallet::fetch_chunks(1..chunk_page_size as usize).unwrap().len(),
chunk_page_size as usize - 1
);
assert_eq!(
PeoplePallet::fetch_chunks(1..(chunk_page_size - 1) as usize).unwrap().len(),
chunk_page_size as usize - 2
);
assert_eq!(
PeoplePallet::fetch_chunks(0..chunk_page_size as usize).unwrap().len(),
chunk_page_size as usize
);
})
}
#[test]
fn multi_page_chunks() {
new_test_ext().execute_with(|| {
let chunk_page_size: u32 = <Test as Config>::ChunkPageSize::get();
let chunks: BoundedVec<(), _> = [(); 4096]
.into_iter()
.take(chunk_page_size as usize)
.collect::<Vec<_>>()
.try_into()
.unwrap();
Chunks::<Test>::insert(0, &chunks);
Chunks::<Test>::insert(1, chunks);
assert_eq!(
PeoplePallet::fetch_chunks(0..(2 * chunk_page_size - 1) as usize).unwrap().len(),
2 * chunk_page_size as usize - 1
);
assert_eq!(
PeoplePallet::fetch_chunks(1..2 * chunk_page_size as usize).unwrap().len(),
2 * chunk_page_size as usize - 1
);
assert_eq!(
PeoplePallet::fetch_chunks(1..(2 * chunk_page_size - 1) as usize).unwrap().len(),
2 * chunk_page_size as usize - 2
);
assert_eq!(
PeoplePallet::fetch_chunks(0..2 * chunk_page_size as usize).unwrap().len(),
2 * chunk_page_size as usize
);
});
}
}
mod merge_rings {
use super::*;
#[test]
fn fails_if_suspension_session_is_in_progress() {
TestExt::new().execute_with(|| {
RingsState::<Test>::set(RingMembersState::Mutating(1));
assert_noop!(
PeoplePallet::merge_rings(RuntimeOrigin::signed(0), 1, 2),
Error::<Test>::SuspensionSessionInProgress
);
});
}
#[test]
fn fails_given_rings_with_the_same_id() {
TestExt::new().execute_with(|| {
assert_noop!(
PeoplePallet::merge_rings(RuntimeOrigin::signed(0), 1, 1),
Error::<Test>::InvalidRing
);
});
}
#[test]
fn fails_given_current_ring_id() {
TestExt::new().execute_with(|| {
CurrentRingIndex::<Test>::set(14);
assert_noop!(
PeoplePallet::merge_rings(RuntimeOrigin::signed(0), 1, 14),
Error::<Test>::InvalidRing
);
assert_noop!(
PeoplePallet::merge_rings(RuntimeOrigin::signed(0), 14, 1),
Error::<Test>::InvalidRing
);
});
}
#[test]
fn fails_when_one_of_rings_is_half_full() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 19);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(build_ring(1, None));
CurrentRingIndex::<Test>::set(14);
assert_noop!(
PeoplePallet::merge_rings(RuntimeOrigin::signed(0), 0, 1),
Error::<Test>::RingAboveMergeThreshold
);
assert_noop!(
PeoplePallet::merge_rings(RuntimeOrigin::signed(0), 1, 0),
Error::<Test>::RingAboveMergeThreshold
);
});
}
#[test]
fn fails_if_one_of_rings_has_pending_suspensions() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 19);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(build_ring(1, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[1, 2, 3, 4, 5, 6];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1, 2, 3, 4, 5, 6]);
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[7, 8];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
CurrentRingIndex::<Test>::set(14);
assert_noop!(
PeoplePallet::merge_rings(RuntimeOrigin::signed(0), 0, 1),
Error::<Test>::SuspensionsPending
);
});
}
#[test]
fn moves_all_keys_from_target_ring_to_base_ring() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 19);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(build_ring(1, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1, 2, 3, 4, 5, 6]);
assert_eq!(suspended_indices_list(1).into_inner(), vec![1, 2, 3, 4, 5, 6]);
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
PeoplePallet::remove_suspended_keys(RI_ZERO);
PeoplePallet::remove_suspended_keys(1);
CurrentRingIndex::<Test>::set(14);
assert_ok!(PeoplePallet::merge_rings(RuntimeOrigin::signed(0), 0, 1));
assert_eq!(RingKeys::<Test>::get(RI_ZERO).len(), 8);
assert_eq!(RingKeysStatus::<Test>::get(RI_ZERO).total, 8);
assert!(Root::<Test>::get(RI_ZERO).is_some());
assert!(Root::<Test>::get(1).is_none());
});
}
}
mod suspensions {
use super::*;
#[test]
fn suspending_personhood_fails_if_no_session_started() {
TestExt::new().execute_with(|| {
let suspensions: &[PersonalId] = &[1];
assert_noop!(
PeoplePallet::suspend_personhood(suspensions),
Error::<Test>::NoMutationSession
);
});
}
#[test]
fn suspending_personhood_fails_if_id_not_in_ring() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[14];
assert_noop!(
PeoplePallet::suspend_personhood(suspensions),
Error::<Test>::InvalidSuspensions
);
});
}
#[test]
fn suspending_personhood_marks_people_as_suspended() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[1];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
let personal_record = People::<Test>::get(1);
assert!(personal_record.is_some());
assert_eq!(personal_record.unwrap().position, RingPosition::Suspended);
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1]);
});
}
#[test]
fn suspended_people_removal_fails_if_session_started() {
TestExt::new().execute_with(|| {
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert!(!PeoplePallet::should_remove_suspended_keys(RI_ZERO, true));
});
}
#[test]
fn suspended_people_removal_fails_if_keys_are_migrated() {
TestExt::new().execute_with(|| {
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert_ok!(PeoplePallet::end_people_set_mutation_session());
assert!(!PeoplePallet::should_remove_suspended_keys(RI_ZERO, true));
});
}
#[test]
fn suspended_people_removal_fails_if_no_suspensions_recorded_in_ring() {
TestExt::new().execute_with(|| {
assert!(!PeoplePallet::should_remove_suspended_keys(RI_ZERO, true));
});
}
#[test]
fn suspended_people_removal_modifies_ring_data() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
let initial_root = Root::<Test>::get(RI_ZERO).unwrap();
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[1];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1]);
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert_eq!(RingKeysStatus::<Test>::get(RI_ZERO), RingStatus { included: 0, total: 9 });
assert_eq!(RingKeys::<Test>::get(RI_ZERO).len(), 9);
assert_ne!(Root::<Test>::get(RI_ZERO).unwrap().intermediate, initial_root.intermediate);
assert!(suspended_indices_list(RI_ZERO).is_empty());
});
}
#[test]
fn suspending_in_multiple_sessions() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert_ok!(PeoplePallet::suspend_personhood(&[1, 2]));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1, 2]);
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert_ok!(PeoplePallet::suspend_personhood(&[3, 4]));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3, 4]);
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert_eq!(RingKeys::<Test>::get(RI_ZERO).len(), 6);
});
}
#[test]
fn suspending_personhood_then_resume() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[1];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
let personal_record = People::<Test>::get(1);
assert!(personal_record.is_some());
assert_eq!(personal_record.unwrap().position, RingPosition::Suspended);
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1]);
assert_ok!(PeoplePallet::recognize_personhood(1, None));
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1]);
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert!(suspended_indices_list(RI_ZERO).is_empty());
assert_ok!(build_ring(RI_ZERO, None));
});
}
#[test]
fn suspending_person_removes_associated_data() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
let person_id = 0;
let account_id = 42;
let id_origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(person_id));
assert_ok!(PeoplePallet::set_personal_id_account(id_origin, account_id, 0));
assert_eq!(AccountToPersonalId::<Test>::get(account_id), Some(person_id));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert_ok!(PeoplePallet::suspend_personhood(&[person_id]));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert!(AccountToPersonalId::<Test>::get(account_id).is_none());
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![person_id as u32]);
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert!(AccountToPersonalId::<Test>::get(account_id).is_none());
let nonce = 0;
let tx_ext = (
AsPerson::<Test>::new(Some(AsPersonInfo::AsPersonalIdentityWithAccount(nonce))),
frame_system::CheckNonce::<Test>::from(nonce),
);
let dummy_call = Call::<Test>::unset_alias_account {};
assert_noop!(
exec_tx(Some(account_id), tx_ext, dummy_call),
InvalidTransaction::BadSigner
);
});
}
}
mod key_migration {
use super::*;
#[test]
fn migrate_key_fails_without_old_key() {
TestExt::new().execute_with(|| {
let secret = Simple::new_secret([0; 32]);
let public = Simple::member_from_secret(&secret);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_noop!(
PeoplePallet::migrate_included_key(origin, public),
Error::<Test>::NotPerson
);
});
}
#[test]
fn migrate_key_fails_for_existing_key() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 19);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
let record3 = People::<Test>::get(3).unwrap();
let record13 = People::<Test>::get(13).unwrap();
assert!(matches!(
record3.position,
RingPosition::Included {
ring_index: 0,
ring_position: 3,
scheduled_for_removal: false
}
));
assert!(matches!(record13.position, RingPosition::Onboarding { queue_page: 0 }));
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_noop!(
PeoplePallet::migrate_included_key(origin.clone(), record3.key),
Error::<Test>::KeyAlreadyInUse
);
assert_noop!(
PeoplePallet::migrate_included_key(origin, record13.key),
Error::<Test>::KeyAlreadyInUse
);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(13));
assert_noop!(
PeoplePallet::migrate_onboarding_key(origin.clone(), record3.key),
Error::<Test>::KeyAlreadyInUse
);
assert_noop!(
PeoplePallet::migrate_onboarding_key(origin, record13.key),
Error::<Test>::KeyAlreadyInUse
);
});
}
#[test]
fn migrate_key_fails_for_suspended_person() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[3];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3]);
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert!(matches!(People::<Test>::get(3).unwrap().position, RingPosition::Suspended));
let new_secret = Simple::new_secret([100; 32]);
let new_public = Simple::member_from_secret(&new_secret);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_noop!(
PeoplePallet::migrate_included_key(origin, new_public),
Error::<Test>::Suspended
);
});
}
#[test]
fn migrate_key_enqueues_included_key_for_removal() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
let initial_record = People::<Test>::get(3).unwrap();
assert!(matches!(
initial_record.position,
RingPosition::Included {
ring_index: 0,
ring_position: 3,
scheduled_for_removal: false
}
));
let temp_secret = Simple::new_secret([100; 32]);
let temp_public = Simple::member_from_secret(&temp_secret);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_ok!(PeoplePallet::migrate_included_key(origin, temp_public));
assert_eq!(KeyMigrationQueue::<Test>::get(3).unwrap(), temp_public);
let temp_record = People::<Test>::get(3).unwrap();
assert_eq!(temp_record.key, initial_record.key);
assert_eq!(
temp_record.position,
RingPosition::Included {
ring_index: 0,
ring_position: 3,
scheduled_for_removal: true
}
);
assert_eq!(Keys::<Test>::get(temp_public).unwrap(), 3);
assert_eq!(Keys::<Test>::get(initial_record.key).unwrap(), 3);
let new_secret = Simple::new_secret([101; 32]);
let new_public = Simple::member_from_secret(&new_secret);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_ok!(PeoplePallet::migrate_included_key(origin, new_public));
assert_eq!(KeyMigrationQueue::<Test>::get(3).unwrap(), new_public);
let new_record = People::<Test>::get(3).unwrap();
assert_eq!(
new_record.position,
RingPosition::Included {
ring_index: 0,
ring_position: 3,
scheduled_for_removal: true
}
);
assert_eq!(Keys::<Test>::get(new_public).unwrap(), 3);
assert_eq!(Keys::<Test>::get(initial_record.key).unwrap(), 3);
assert!(!Keys::<Test>::contains_key(temp_public));
});
}
#[test]
fn migrate_key_replaces_onboarding_key() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 19);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
let initial_record = People::<Test>::get(13).unwrap();
assert!(matches!(initial_record.position, RingPosition::Onboarding { queue_page: 0 }));
let initial_pos = OnboardingQueue::<Test>::get(0)
.iter()
.position(|k| *k == initial_record.key)
.unwrap();
let new_secret = Simple::new_secret([100; 32]);
let new_public = Simple::member_from_secret(&new_secret);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(13));
assert_ok!(PeoplePallet::migrate_onboarding_key(origin, new_public));
let personal_record = People::<Test>::get(13).unwrap();
assert_eq!(personal_record.position, RingPosition::Onboarding { queue_page: 0 });
assert_eq!(OnboardingQueue::<Test>::get(0)[initial_pos], new_public);
assert_eq!(Keys::<Test>::get(new_public).unwrap(), 13);
assert!(!Keys::<Test>::contains_key(initial_record.key));
let new_secret = Simple::new_secret([101; 32]);
let new_public = Simple::member_from_secret(&new_secret);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(13));
let temp_record = personal_record;
assert_ok!(PeoplePallet::migrate_onboarding_key(origin, new_public));
assert_eq!(Keys::<Test>::get(new_public).unwrap(), 13);
assert_eq!(OnboardingQueue::<Test>::get(0)[initial_pos], new_public);
assert!(!Keys::<Test>::contains_key(temp_record.key));
});
}
#[test]
fn migrate_key_invalid_state() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 19);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
let new_secret = Simple::new_secret([100; 32]);
let new_public = Simple::member_from_secret(&new_secret);
let initial_record = People::<Test>::get(13).unwrap();
assert!(matches!(initial_record.position, RingPosition::Onboarding { queue_page: 0 }));
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(13));
assert_noop!(
PeoplePallet::migrate_included_key(origin, new_public),
Error::<Test>::InvalidKeyMigration
);
let initial_record = People::<Test>::get(3).unwrap();
assert!(matches!(
initial_record.position,
RingPosition::Included {
ring_index: 0,
ring_position: 3,
scheduled_for_removal: false
}
));
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_noop!(
PeoplePallet::migrate_onboarding_key(origin, new_public),
Error::<Test>::InvalidKeyMigration
);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_ok!(PeoplePallet::migrate_included_key(origin, new_public));
let new_secret = Simple::new_secret([101; 32]);
let new_public = Simple::member_from_secret(&new_secret);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(13));
assert_ok!(PeoplePallet::migrate_onboarding_key(origin, new_public));
});
}
#[test]
fn migrate_keys_mutates_record() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
let initial_record = People::<Test>::get(3).unwrap();
assert!(matches!(
initial_record.position,
RingPosition::Included {
ring_index: 0,
ring_position: 3,
scheduled_for_removal: false
}
));
let secret1 = Simple::new_secret([100; 32]);
let public1 = Simple::member_from_secret(&secret1);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_ok!(PeoplePallet::migrate_included_key(origin, public1));
assert_eq!(KeyMigrationQueue::<Test>::get(3).unwrap(), public1);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_eq!(Keys::<Test>::get(public1).unwrap(), 3);
let new_record = People::<Test>::get(3).unwrap();
assert_eq!(new_record.key, public1);
assert!(matches!(new_record.position, RingPosition::Onboarding { queue_page: 0 }));
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3]);
let secret2 = Simple::new_secret([101; 32]);
let public2 = Simple::member_from_secret(&secret2);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(4));
assert_ok!(PeoplePallet::migrate_included_key(origin, public2));
assert_eq!(KeyMigrationQueue::<Test>::get(4).unwrap(), public2);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_eq!(Keys::<Test>::get(public2).unwrap(), 4);
let new_record = People::<Test>::get(4).unwrap();
assert_eq!(new_record.key, public2);
assert!(matches!(new_record.position, RingPosition::Onboarding { queue_page: 0 }));
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3, 4]);
});
}
#[test]
fn migrate_keys_before_suspension() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
let initial_record = People::<Test>::get(3).unwrap();
assert!(matches!(
initial_record.position,
RingPosition::Included {
ring_index: 0,
ring_position: 3,
scheduled_for_removal: false
}
));
let secret1 = Simple::new_secret([100; 32]);
let public1 = Simple::member_from_secret(&secret1);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_ok!(PeoplePallet::migrate_included_key(origin.clone(), public1));
assert_eq!(KeyMigrationQueue::<Test>::get(3).unwrap(), public1);
assert!(suspended_indices_list(RI_ZERO).is_empty());
assert!(!RingsState::<Test>::get().key_migration());
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert!(!RingsState::<Test>::get().key_migration());
assert!(suspended_indices_list(RI_ZERO).is_empty());
assert_ok!(PeoplePallet::suspend_personhood(&[3]));
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3]);
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3]);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(4));
let secret2 = Simple::new_secret([101; 32]);
let public2 = Simple::member_from_secret(&secret2);
assert_ok!(PeoplePallet::migrate_included_key(origin, public2));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert!(!RingsState::<Test>::get().key_migration());
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3]);
assert_ok!(PeoplePallet::suspend_personhood(&[4]));
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3, 4]);
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3, 4]);
});
}
#[test]
fn migrate_keys_after_suspension() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
let initial_record = People::<Test>::get(3).unwrap();
assert!(matches!(
initial_record.position,
RingPosition::Included {
ring_index: 0,
ring_position: 3,
scheduled_for_removal: false
}
));
let secret1 = Simple::new_secret([100; 32]);
let public1 = Simple::member_from_secret(&secret1);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_ok!(PeoplePallet::migrate_included_key(origin.clone(), public1));
assert_eq!(KeyMigrationQueue::<Test>::get(3).unwrap(), public1);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert_ok!(PeoplePallet::suspend_personhood(&[3]));
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3]);
assert!(!RingsState::<Test>::get().key_migration());
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3]);
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3]);
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert!(suspended_indices_list(RI_ZERO).is_empty());
let secret2 = Simple::new_secret([101; 32]);
let public2 = Simple::member_from_secret(&secret2);
assert_noop!(
PeoplePallet::migrate_included_key(origin, public2),
Error::<Test>::Suspended
);
});
}
#[test]
fn migrate_key_and_remove_it() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
let initial_record = People::<Test>::get(3).unwrap();
assert!(matches!(
initial_record.position,
RingPosition::Included {
ring_index: 0,
ring_position: 3,
scheduled_for_removal: false
}
));
let secret1 = Simple::new_secret([100; 32]);
let public1 = Simple::member_from_secret(&secret1);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(3));
assert_ok!(PeoplePallet::migrate_included_key(origin.clone(), public1));
assert_eq!(KeyMigrationQueue::<Test>::get(3).unwrap(), public1);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert!(!RingsState::<Test>::get().key_migration());
assert!(suspended_indices_list(RI_ZERO).is_empty());
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![3]);
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert!(suspended_indices_list(RI_ZERO).is_empty());
assert_eq!(RingKeys::<Test>::get(0).len(), 9);
assert!(!RingKeys::<Test>::get(0)
.into_iter()
.any(|k| k == initial_record.key || k == public1));
});
}
#[test]
fn migrate_and_suspend_keys_then_remove_them() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
for personal_id in 0..10 {
assert_eq!(
People::<Test>::get(personal_id).unwrap().position.ring_index().unwrap(),
RI_ZERO
);
}
let old_keys = RingKeys::<Test>::get(RI_ZERO);
let new_keys: Vec<_> = (100..110)
.map(|x| Simple::new_secret([x; 32]))
.map(|s| Simple::member_from_secret(&s))
.collect();
let static_people: Vec<PersonalId> = (0..10).filter(|i| i % 3 == 2).collect();
let migrated_people: Vec<PersonalId> = (0..10).filter(|i| i % 3 == 0).collect();
for personal_id in &migrated_people {
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(*personal_id));
assert_ok!(PeoplePallet::migrate_included_key(
origin.clone(),
new_keys[*personal_id as usize]
));
}
assert!(suspended_indices_list(RI_ZERO).is_empty());
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: Vec<PersonalId> = (0..10).filter(|i| i % 3 == 1).collect();
assert_ok!(PeoplePallet::suspend_personhood(&suspensions[..]));
assert_eq!(
suspended_indices_list(RI_ZERO).into_inner(),
suspensions.iter().copied().map(|x| x as u32).collect::<Vec<_>>()
);
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_eq!(
suspended_indices_list(RI_ZERO).len(),
migrated_people.len() + suspensions.len()
);
let suspended_indices = suspended_indices_list(RI_ZERO);
assert!(&suspended_indices[..].windows(2).all(|pair| pair[0] < pair[1]));
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert!(suspended_indices_list(RI_ZERO).is_empty());
let current_keys = RingKeys::<Test>::get(0).into_inner();
assert_eq!(current_keys.len(), static_people.len());
let static_keys: Vec<_> = old_keys
.into_iter()
.enumerate()
.filter(|(i, _)| static_people.contains(&(*i as PersonalId)))
.map(|(_, k)| k)
.collect();
assert_eq!(current_keys, static_keys);
});
}
}
mod onboard_people {
use super::*;
#[test]
fn fails_if_suspensions_ongoing() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert_noop!(PeoplePallet::onboard_people(), Error::<Test>::Incomplete);
});
}
#[test]
fn fails_if_ring_contains_suspended_members() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
OnboardingSize::<Test>::set(5);
generate_people_with_index(0, 4);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[1, 2, 3];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
let db_weight: RuntimeDbWeight = <Test as frame_system::Config>::DbWeight::get();
let end_migrate_db_weight = db_weight.reads_writes(2, 1);
let mut expected_consumed = end_migrate_db_weight;
assert_eq!(meter.consumed(), expected_consumed);
let suspended_indices = vec![1, 2, 3];
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), suspended_indices);
PeoplePallet::remove_suspended_keys(RI_ZERO);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[4];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
expected_consumed += end_migrate_db_weight;
assert_eq!(meter.consumed(), expected_consumed);
generate_people_with_index(10, 22);
assert_noop!(PeoplePallet::onboard_people(), Error::<Test>::Incomplete);
});
}
#[test]
fn no_keys_in_queue() {
TestExt::new().execute_with(|| {
assert_noop!(PeoplePallet::onboard_people(), Error::<Test>::Incomplete);
});
}
#[test]
fn less_keys_in_queue_than_onboarding_size() {
TestExt::new().execute_with(|| {
OnboardingSize::<Test>::set(5);
generate_people_with_index(0, 3);
assert_noop!(PeoplePallet::onboard_people(), Error::<Test>::Incomplete);
});
}
#[test]
fn current_ring_empty() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
let queue = OnboardingQueue::<Test>::get(RI_ZERO);
assert_eq!(queue.len(), 0);
assert_eq!(RingKeys::<Test>::get(RI_ZERO).len(), 10);
assert_eq!(RingKeysStatus::<Test>::get(RI_ZERO), RingStatus { total: 10, included: 0 });
for id in 0..=9 {
let record = People::<Test>::get(id);
assert!(record.is_some());
match record.unwrap().position {
RingPosition::Included { ring_index, .. } => {
assert_eq!(ring_index, RI_ZERO);
},
_ => panic!("Expected RingPosition::Included variant"),
}
}
assert_eq!(CurrentRingIndex::<Test>::get(), RI_ZERO + 1);
});
}
#[test]
fn current_ring_with_keys_in_it() {
TestExt::new().execute_with(|| {
OnboardingSize::<Test>::set(4);
generate_people_with_index(0, 4);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert!(Root::<Test>::get(RI_ZERO).is_some());
generate_people_with_index(5, 9);
assert_ok!(PeoplePallet::onboard_people());
assert!(Root::<Test>::get(RI_ZERO + 1).is_none());
assert_eq!(RingKeys::<Test>::get(RI_ZERO).len(), 10);
assert_eq!(RingKeysStatus::<Test>::get(RI_ZERO), RingStatus { total: 10, included: 5 });
});
}
#[test]
fn multiple_queue_pages() {
TestExt::new().execute_with(|| {
let queue_page_size: u32 = <Test as Config>::OnboardingQueuePageSize::get();
let onboarding_size: u32 = OnboardingSize::<Test>::get();
let people_to_onboard = queue_page_size * 4;
let expected_pages_to_fill: u32 = people_to_onboard / queue_page_size;
let expected_rings_to_build: u32 = people_to_onboard / onboarding_size;
assert_eq!(OnboardingQueue::<Test>::get(0).len(), 0);
generate_people_with_index(1, people_to_onboard as u8);
for page_index in 0..=expected_pages_to_fill - 1 {
assert_eq!(
OnboardingQueue::<Test>::get(page_index).len(),
queue_page_size as usize
);
}
for _ in 0..=expected_rings_to_build - 1 {
assert_ok!(PeoplePallet::onboard_people());
}
for page_index in 0..=expected_pages_to_fill - 1 {
assert_eq!(OnboardingQueue::<Test>::get(page_index).len(), 0);
}
let mut personal_id: u32 = 1;
for ring_index in 0..=expected_rings_to_build - 1 {
assert_eq!(RingKeys::<Test>::get(ring_index).len(), 10);
assert_eq!(
RingKeysStatus::<Test>::get(ring_index),
RingStatus { total: 10, included: 0 }
);
while personal_id < onboarding_size {
let record = People::<Test>::get(personal_id as u64);
assert!(record.is_some());
match record.unwrap().position {
RingPosition::Included { ring_index: record_ring_index, .. } => {
assert_eq!(record_ring_index, ring_index, "{personal_id}");
},
_ => panic!("Expected RingPosition::Included variant"),
}
personal_id += 1;
}
}
for ring_index in 0..=expected_rings_to_build - 1 {
assert_ok!(build_ring(ring_index, None));
assert!(Root::<Test>::get(ring_index).is_some());
assert_eq!(RingKeys::<Test>::get(ring_index).len(), 10);
assert_eq!(
RingKeysStatus::<Test>::get(ring_index),
RingStatus { total: 10, included: 10 }
);
while personal_id < onboarding_size {
let record = People::<Test>::get(personal_id as u64);
assert!(record.is_some());
match record.unwrap().position {
RingPosition::Included { ring_index: record_ring_index, .. } => {
assert_eq!(record_ring_index, ring_index, "{personal_id}");
},
_ => panic!("Expected RingPosition::Included variant"),
}
personal_id += 1;
}
}
});
}
#[test]
fn awaiting_people_in_between_rings_boundaries() {
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
generate_people_with_index(1, 30);
for _ in 1..=3 {
assert_ok!(PeoplePallet::onboard_people());
}
for ring_index in 0..=2 {
assert_ok!(build_ring(ring_index, None));
}
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: Vec<PersonalId> = vec![1, 11, 21];
assert_ok!(PeoplePallet::suspend_personhood(&suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
for ring_index in 0..=2 {
assert_eq!(
PeoplePallet::remove_suspended_keys(ring_index),
<Test as Config>::WeightInfo::remove_suspended_people(10)
);
}
for ring_index in 0..=2 {
assert_ok!(build_ring(ring_index, None));
assert!(Root::<Test>::get(ring_index).is_some());
assert_eq!(RingKeys::<Test>::get(ring_index).len(), 9);
assert_eq!(
RingKeysStatus::<Test>::get(ring_index),
RingStatus { total: 9, included: 9 }
);
}
generate_people_with_index(31, 40);
assert_eq!(OnboardingQueue::<Test>::get(0).len(), 10);
assert_ok!(PeoplePallet::onboard_people());
assert_eq!(OnboardingQueue::<Test>::get(0).len(), 0);
for ring_index in 0..=2 {
assert!(Root::<Test>::get(ring_index).is_some());
assert_eq!(RingKeys::<Test>::get(ring_index).len(), 9);
assert_eq!(
RingKeysStatus::<Test>::get(ring_index),
RingStatus { total: 9, included: 9 }
);
}
assert_eq!(RingKeys::<Test>::get(3).len(), 10);
assert_eq!(RingKeysStatus::<Test>::get(3), RingStatus { total: 10, included: 0 });
});
}
#[test]
fn onboarding_queue_index_rotates_when_overflown() {
TestExt::new().execute_with(|| {
QueuePageIndices::<Test>::set((u32::MAX - 2, u32::MAX - 1));
let queue_page_size: u32 = <Test as Config>::OnboardingQueuePageSize::get();
let people_to_onboard: u8 = (queue_page_size * 3) as u8;
generate_people_with_index(1, people_to_onboard);
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, u32::MAX - 2);
assert_eq!(tail, 0);
let onboarding_size: u32 = OnboardingSize::<Test>::get();
let expected_rings_to_build: u32 = people_to_onboard as u32 / onboarding_size;
for _ in 0..=expected_rings_to_build - 1 {
assert_ok!(PeoplePallet::onboard_people());
}
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, 0);
assert_eq!(tail, 0);
generate_people_with_index(
(queue_page_size * 3) as u8 + 1,
(queue_page_size * 6) as u8,
);
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, 0);
assert_eq!(tail, 2);
assert!(
!OnboardingQueue::<Test>::get(head).is_empty() &&
!OnboardingQueue::<Test>::get(tail).is_empty()
);
for _ in 0..=expected_rings_to_build - 1 {
assert_ok!(PeoplePallet::onboard_people());
}
assert_eq!(head, 0);
assert_eq!(tail, 2);
});
}
#[test]
fn multi_paged_queue_left_with_one_page_after_single_onboard_call() {
let queue_page_size: u32 = <Test as Config>::OnboardingQueuePageSize::get();
let people_to_onboard = queue_page_size * 3;
TestExt::new().max_ring_size(queue_page_size * 2).execute_with(|| {
OnboardingSize::<Test>::set(queue_page_size * 2);
generate_people_with_index(1, people_to_onboard as u8);
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, 0);
assert_eq!(tail, 2);
assert_eq!(OnboardingQueue::<Test>::get(0).len(), queue_page_size as usize);
assert_eq!(OnboardingQueue::<Test>::get(1).len(), queue_page_size as usize);
assert_eq!(OnboardingQueue::<Test>::get(2).len(), queue_page_size as usize);
assert_ok!(PeoplePallet::onboard_people());
assert_eq!(ActiveMembers::<Test>::get(), queue_page_size * 2);
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, 2);
assert_eq!(tail, 2);
assert_eq!(OnboardingQueue::<Test>::get(0).len(), 0);
assert_eq!(OnboardingQueue::<Test>::get(1).len(), 0);
assert_eq!(OnboardingQueue::<Test>::get(2).len(), queue_page_size as usize);
generate_people_with_index(
people_to_onboard as u8 + 1,
people_to_onboard as u8 + queue_page_size as u8,
);
assert_eq!(OnboardingQueue::<Test>::get(0).len(), 0);
assert_eq!(OnboardingQueue::<Test>::get(1).len(), 0);
assert_eq!(OnboardingQueue::<Test>::get(2).len(), queue_page_size as usize);
assert_eq!(OnboardingQueue::<Test>::get(3).len(), queue_page_size as usize);
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, 2);
assert_eq!(tail, 3);
assert_ok!(PeoplePallet::onboard_people());
assert_eq!(ActiveMembers::<Test>::get(), queue_page_size * 4);
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, 3);
assert_eq!(tail, 3);
for i in 0..3 {
assert_eq!(OnboardingQueue::<Test>::get(i).len(), 0);
}
generate_people_with_index(
people_to_onboard as u8 + queue_page_size as u8 + 1,
people_to_onboard as u8 + (queue_page_size as u8 * 2),
);
assert_eq!(OnboardingQueue::<Test>::get(3).len(), queue_page_size as usize);
assert_eq!(OnboardingQueue::<Test>::get(4).len(), 0);
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, 3);
assert_eq!(tail, 3);
});
}
#[test]
fn multi_paged_queue_left_with_no_queue_page_after_single_onboard_call() {
let queue_page_size: u32 = <Test as Config>::OnboardingQueuePageSize::get();
let people_to_onboard = queue_page_size * 4;
let onboarding_size = 5;
TestExt::new().execute_with(|| {
let mut meter = WeightMeter::new();
crate::OnboardingSize::<Test>::set(onboarding_size);
generate_people_with_index(1, people_to_onboard as u8);
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, 0);
assert_eq!(tail, 3);
assert_eq!(OnboardingQueue::<Test>::get(0).len(), queue_page_size as usize);
assert_eq!(OnboardingQueue::<Test>::get(1).len(), queue_page_size as usize);
assert_eq!(OnboardingQueue::<Test>::get(2).len(), queue_page_size as usize);
assert_eq!(OnboardingQueue::<Test>::get(3).len(), queue_page_size as usize);
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, 0);
assert_eq!(tail, 3);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let mut suspensions: Vec<PersonalId> = (3u64..queue_page_size as PersonalId).collect();
suspensions
.extend(2 + queue_page_size as PersonalId..2u64 * queue_page_size as PersonalId);
assert_ok!(PeoplePallet::suspend_personhood(&suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
let db_weight: RuntimeDbWeight = <Test as frame_system::Config>::DbWeight::get();
let end_migrate_db_weight = db_weight.reads_writes(2, 1);
assert_eq!(meter.consumed(), end_migrate_db_weight);
assert_eq!(OnboardingQueue::<Test>::get(0).len(), 3);
assert_eq!(OnboardingQueue::<Test>::get(1).len(), 2);
assert_eq!(OnboardingQueue::<Test>::get(2).len(), queue_page_size as usize);
assert_eq!(OnboardingQueue::<Test>::get(3).len(), queue_page_size as usize);
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, 0);
assert_eq!(tail, 3);
assert_ok!(PeoplePallet::onboard_people());
assert_eq!(ActiveMembers::<Test>::get(), onboarding_size);
assert_eq!(OnboardingQueue::<Test>::get(0).len(), 0);
assert_eq!(OnboardingQueue::<Test>::get(1).len(), 0);
let (head, tail) = QueuePageIndices::<Test>::get();
assert_eq!(head, 2);
assert_eq!(tail, 3);
assert_eq!(OnboardingQueue::<Test>::get(2).len(), queue_page_size as usize);
});
}
}
mod merge_queue_pages {
use super::*;
use frame_support::pallet_prelude::Get;
#[test]
fn fails_if_queue_empty() {
TestExt::new().execute_with(|| {
assert!(matches!(PeoplePallet::should_merge_queue_pages(), QueueMergeAction::NoAction));
});
}
#[test]
fn fails_if_only_one_page_exists() {
TestExt::new().execute_with(|| {
generate_people_with_index(0, 8);
assert!(matches!(PeoplePallet::should_merge_queue_pages(), QueueMergeAction::NoAction));
});
}
#[test]
fn fails_if_too_many_elements_per_page() {
TestExt::new().execute_with(|| {
let queue_size: u32 = <Test as Config>::OnboardingQueuePageSize::get();
let mut meter = WeightMeter::new();
generate_people_with_index(1, queue_size as u8 * 2);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let first_page_suspensions: Vec<PersonalId> = (1..20).collect();
assert_ok!(PeoplePallet::suspend_personhood(&first_page_suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
let db_weight: RuntimeDbWeight = <Test as frame_system::Config>::DbWeight::get();
let end_migrate_db_weight = db_weight.reads_writes(2, 1);
assert_eq!(meter.consumed(), end_migrate_db_weight);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let second_page_suspensions: Vec<PersonalId> = (40..60).collect();
assert_ok!(PeoplePallet::suspend_personhood(&second_page_suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
assert!(matches!(PeoplePallet::should_merge_queue_pages(), QueueMergeAction::NoAction));
});
}
#[test]
fn succeeds_if_only_two_pages_exist() {
TestExt::new().execute_with(|| {
let queue_size: u32 = <Test as Config>::OnboardingQueuePageSize::get();
let mut meter = WeightMeter::new();
generate_people_with_index(1, queue_size as u8 * 2);
assert_eq!(QueuePageIndices::<Test>::get(), (0, 1));
assert!(OnboardingQueue::<Test>::get(0).is_full());
assert!(OnboardingQueue::<Test>::get(1).is_full());
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let first_page_suspensions: Vec<PersonalId> = (0..19).collect();
assert_ok!(PeoplePallet::suspend_personhood(&first_page_suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
let db_weight: RuntimeDbWeight = <Test as frame_system::Config>::DbWeight::get();
let end_migrate_db_weight = db_weight.reads_writes(2, 1);
let mut expected_consumed = end_migrate_db_weight;
assert_eq!(meter.consumed(), expected_consumed);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let second_page_suspensions: Vec<PersonalId> = (40..61).collect();
assert_ok!(PeoplePallet::suspend_personhood(&second_page_suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
expected_consumed += end_migrate_db_weight;
assert_eq!(meter.consumed(), expected_consumed);
let QueueMergeAction::Merge { initial_head, new_head, first_key_page, second_key_page } =
PeoplePallet::should_merge_queue_pages()
else {
panic!("should be mergeable")
};
PeoplePallet::merge_queue_pages(
initial_head,
new_head,
first_key_page,
second_key_page,
);
assert_eq!(QueuePageIndices::<Test>::get(), (1, 1));
assert!(OnboardingQueue::<Test>::get(0).is_empty());
let page = OnboardingQueue::<Test>::get(1);
assert!(page.is_full());
let first_page_remaining: Vec<PersonalId> = (19..40).collect();
let second_page_remaining: Vec<PersonalId> = (61..80).collect();
let mut page_iter = page.iter();
for id in first_page_remaining.iter().chain(second_page_remaining.iter()) {
let key = page_iter.next();
assert!(key.is_some());
let key = key.unwrap();
let personal_id_from_page = Keys::<Test>::get(key).unwrap();
assert_eq!(*id, personal_id_from_page);
let personal_record = People::<Test>::get(personal_id_from_page).unwrap();
assert_eq!(personal_record.position, RingPosition::Onboarding { queue_page: 1 });
}
});
}
#[test]
fn succeeds_if_multiple_pages_exist() {
TestExt::new().execute_with(|| {
let queue_size: u32 = <Test as Config>::OnboardingQueuePageSize::get();
let mut meter = WeightMeter::new();
generate_people_with_index(1, queue_size as u8 * 3);
assert_eq!(QueuePageIndices::<Test>::get(), (0, 2));
assert!(OnboardingQueue::<Test>::get(0).is_full());
assert!(OnboardingQueue::<Test>::get(1).is_full());
assert!(OnboardingQueue::<Test>::get(2).is_full());
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let first_page_suspensions: Vec<PersonalId> = (0..19).collect();
assert_ok!(PeoplePallet::suspend_personhood(&first_page_suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
let db_weight: RuntimeDbWeight = <Test as frame_system::Config>::DbWeight::get();
let end_migrate_db_weight = db_weight.reads_writes(2, 1);
let mut expected_consumed = end_migrate_db_weight;
assert_eq!(meter.consumed(), expected_consumed);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let second_page_suspensions: Vec<PersonalId> = (40..61).collect();
assert_ok!(PeoplePallet::suspend_personhood(&second_page_suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
expected_consumed += end_migrate_db_weight;
assert_eq!(meter.consumed(), expected_consumed);
let QueueMergeAction::Merge { initial_head, new_head, first_key_page, second_key_page } =
PeoplePallet::should_merge_queue_pages()
else {
panic!("should be mergeable")
};
PeoplePallet::merge_queue_pages(
initial_head,
new_head,
first_key_page,
second_key_page,
);
assert_eq!(QueuePageIndices::<Test>::get(), (1, 2));
assert!(OnboardingQueue::<Test>::get(0).is_empty());
assert!(OnboardingQueue::<Test>::get(2).is_full());
let page = OnboardingQueue::<Test>::get(1);
assert!(page.is_full());
let first_page_remaining: Vec<PersonalId> = (19..40).collect();
let second_page_remaining: Vec<PersonalId> = (61..80).collect();
let mut page_iter = page.iter();
for id in first_page_remaining.iter().chain(second_page_remaining.iter()) {
let key = page_iter.next();
assert!(key.is_some());
let key = key.unwrap();
let personal_id_from_page = Keys::<Test>::get(key).unwrap();
assert_eq!(*id, personal_id_from_page);
let personal_record = People::<Test>::get(personal_id_from_page).unwrap();
assert_eq!(personal_record.position, RingPosition::Onboarding { queue_page: 1 });
}
});
}
}
#[test]
fn test_revision_in_tx_ext_as_alias_account() {
new_test_ext().execute_with(|| {
crate::OnboardingSize::<Test>::set(1);
let (_, pk, sk) = generate_people_with_index(0, 0).pop().unwrap();
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(0, None));
let alias_account = 37;
setup_alias_account(&pk, &sk, MOCK_CONTEXT, alias_account);
let call = frame_system::Call::remark { remark: vec![] };
assert_ok!(exec_as_alias_tx(alias_account, call));
crate::Root::<Test>::mutate(0, |root| {
root.as_mut().unwrap().revision = 1;
});
let call = frame_system::Call::remark { remark: vec![] };
assert_noop!(exec_as_alias_tx(alias_account, call), BadSigner);
});
}
mod poll {
use super::*;
#[test]
fn on_poll_migrates_keys() {
new_test_ext().execute_with(|| {
generate_people_with_index(0, 9);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
for i in 0..5 {
let new_secret = Simple::new_secret([100 + i; 32]);
let new_public = Simple::member_from_secret(&new_secret);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(i as PersonalId));
assert_ok!(PeoplePallet::migrate_included_key(origin, new_public));
assert_eq!(KeyMigrationQueue::<Test>::get(i as PersonalId).unwrap(), new_public);
}
let db_weight: RuntimeDbWeight = <Test as frame_system::Config>::DbWeight::get();
let db_weight = db_weight.reads_writes(1, 1);
let base_weight = <<Test as Config>::WeightInfo as crate::WeightInfo>::on_poll_base();
let step_migration_weight =
<<Test as Config>::WeightInfo as crate::WeightInfo>::migrate_keys_single_included_key()
.saturating_add(db_weight);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
assert_ok!(PeoplePallet::end_people_set_mutation_session());
let mut meter = WeightMeter::with_limit(base_weight);
PeoplePallet::on_poll(0, &mut meter);
assert_eq!(KeyMigrationQueue::<Test>::iter().count(), 5);
assert!(RingsState::<Test>::get().key_migration());
let mut meter = WeightMeter::with_limit(base_weight + step_migration_weight);
PeoplePallet::on_poll(0, &mut meter);
assert_eq!(KeyMigrationQueue::<Test>::iter().count(), 4);
assert!(RingsState::<Test>::get().key_migration());
let mut meter =
WeightMeter::with_limit(base_weight + step_migration_weight.saturating_mul(4));
PeoplePallet::on_poll(0, &mut meter);
assert_eq!(KeyMigrationQueue::<Test>::iter().count(), 0);
assert!(RingsState::<Test>::get().key_migration());
let mut meter = WeightMeter::with_limit(base_weight + step_migration_weight);
PeoplePallet::on_poll(0, &mut meter);
assert_eq!(KeyMigrationQueue::<Test>::iter().count(), 0);
assert!(RingsState::<Test>::get().append_only());
let db_weight: RuntimeDbWeight = <Test as frame_system::Config>::DbWeight::get();
let db_weight = db_weight.reads_writes(2, 1);
assert_eq!(meter.consumed(), base_weight + db_weight);
});
}
#[test]
fn on_poll_removes_stale_keys_once() {
new_test_ext().execute_with(|| {
OnboardingSize::<Test>::put(1);
generate_people_with_index(0, 17);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(build_ring(1, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[1, 2, 3, 4, 5, 11, 12, 13, 14, 15];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1, 2, 3, 4, 5]);
assert_eq!(suspended_indices_list(1).into_inner(), vec![1, 2, 3, 4, 5]);
assert_ok!(PeoplePallet::end_people_set_mutation_session());
RingsState::<Test>::put(RingMembersState::AppendOnly);
let base_weight = <<Test as Config>::WeightInfo as crate::WeightInfo>::on_poll_base();
let step_remove_keys_weight =
<<Test as Config>::WeightInfo as crate::WeightInfo>::remove_suspended_people(
<Test as Config>::MaxRingSize::get(),
);
let mut meter = WeightMeter::with_limit(base_weight);
PeoplePallet::on_poll(0, &mut meter);
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1, 2, 3, 4, 5]);
assert_eq!(suspended_indices_list(1).into_inner(), vec![1, 2, 3, 4, 5]);
let mut meter = WeightMeter::new();
PeoplePallet::on_poll(0, &mut meter);
assert_eq!(PendingSuspensions::<Test>::iter().count(), 1);
let mut actual_consumed = meter.consumed();
let mut meter = WeightMeter::with_limit(base_weight + step_remove_keys_weight);
PeoplePallet::on_poll(0, &mut meter);
assert_eq!(PendingSuspensions::<Test>::iter().count(), 0);
actual_consumed += meter.consumed();
assert_eq!(
actual_consumed,
base_weight
.saturating_mul(2)
.saturating_add(
<<Test as Config>::WeightInfo as crate::WeightInfo>::remove_suspended_people(
10,
)
)
.saturating_add(
<<Test as Config>::WeightInfo as crate::WeightInfo>::remove_suspended_people(8)
)
);
});
}
#[test]
fn on_poll_merges_queue_pages() {
new_test_ext().execute_with(|| {
let queue_size: u32 = <Test as Config>::OnboardingQueuePageSize::get();
let mut meter = WeightMeter::new();
generate_people_with_index(1, queue_size as u8 * 2);
assert_eq!(QueuePageIndices::<Test>::get(), (0, 1));
assert!(OnboardingQueue::<Test>::get(0).is_full());
assert!(OnboardingQueue::<Test>::get(1).is_full());
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let first_page_suspensions: Vec<PersonalId> = (0..19).collect();
assert_ok!(PeoplePallet::suspend_personhood(&first_page_suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let second_page_suspensions: Vec<PersonalId> = (40..61).collect();
assert_ok!(PeoplePallet::suspend_personhood(&second_page_suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
PeoplePallet::migrate_keys(&mut meter);
assert!(matches!(
PeoplePallet::should_merge_queue_pages(),
QueueMergeAction::Merge { .. }
));
let base_weight = <<Test as Config>::WeightInfo as crate::WeightInfo>::on_poll_base();
let merge_pages_weight =
<<Test as Config>::WeightInfo as crate::WeightInfo>::merge_queue_pages();
let mut meter = WeightMeter::with_limit(base_weight);
PeoplePallet::on_poll(0, &mut meter);
assert_eq!(QueuePageIndices::<Test>::get(), (0, 1));
let mut meter = WeightMeter::with_limit(base_weight + merge_pages_weight);
PeoplePallet::on_poll(0, &mut meter);
assert_eq!(QueuePageIndices::<Test>::get(), (1, 1));
assert!(OnboardingQueue::<Test>::get(0).is_empty());
let page = OnboardingQueue::<Test>::get(1);
assert!(page.is_full());
});
}
}
#[test]
fn on_poll_works() {
new_test_ext().execute_with(|| {
let queue_size: u32 = <Test as Config>::OnboardingQueuePageSize::get();
let ring_size: u32 = <Test as Config>::MaxRingSize::get();
OnboardingSize::<Test>::put(1);
generate_people_with_index(1, 2 * ring_size as u8 + 2 * queue_size as u8);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(build_ring(1, None));
assert_eq!(QueuePageIndices::<Test>::get(), (0, 2));
for i in 0..5 {
let new_secret = Simple::new_secret([250 + i; 32]);
let new_public = Simple::member_from_secret(&new_secret);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalIdentity(i as PersonalId));
assert_ok!(PeoplePallet::migrate_included_key(origin, new_public));
assert_eq!(KeyMigrationQueue::<Test>::get(i as PersonalId).unwrap(), new_public);
}
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[1, 2, 3, 4, 5, 11, 12, 13, 14, 15];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1, 2, 3, 4, 5]);
assert_eq!(suspended_indices_list(1).into_inner(), vec![1, 2, 3, 4, 5]);
let second_page_suspensions: Vec<PersonalId> = (41..61).collect();
assert_ok!(PeoplePallet::suspend_personhood(&second_page_suspensions));
assert_ok!(PeoplePallet::end_people_set_mutation_session());
assert_eq!(QueuePageIndices::<Test>::get(), (0, 2));
let mut meter = WeightMeter::new();
PeoplePallet::on_poll(0, &mut meter);
assert_eq!(KeyMigrationQueue::<Test>::iter().count(), 0);
assert!(RingsState::<Test>::get().append_only());
assert_eq!(PendingSuspensions::<Test>::iter().count(), 1);
assert_eq!(QueuePageIndices::<Test>::get(), (1, 2));
assert!(OnboardingQueue::<Test>::get(0).is_empty());
let page = OnboardingQueue::<Test>::get(1);
assert!(page.is_full());
let db_weight: RuntimeDbWeight = <Test as frame_system::Config>::DbWeight::get();
let migrate_db_weight = db_weight.reads_writes(1, 1);
let end_migrate_db_weight = db_weight.reads_writes(2, 1);
let base_weight = <<Test as Config>::WeightInfo as crate::WeightInfo>::on_poll_base();
let step_migration_weight =
<<Test as Config>::WeightInfo as crate::WeightInfo>::migrate_keys_single_included_key()
.saturating_add(migrate_db_weight);
let step_remove_keys_weight =
<<Test as Config>::WeightInfo as crate::WeightInfo>::remove_suspended_people(ring_size);
let merge_pages_weight =
<<Test as Config>::WeightInfo as crate::WeightInfo>::merge_queue_pages();
let expected_consumed = base_weight +
step_migration_weight.saturating_mul(5) +
end_migrate_db_weight +
step_remove_keys_weight +
merge_pages_weight;
assert_eq!(meter.consumed(), expected_consumed);
});
}
#[test]
fn on_idle_works() {
new_test_ext().execute_with(|| {
OnboardingSize::<Test>::put(1);
generate_people_with_index(0, 27);
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
assert_ok!(build_ring(1, None));
assert_ok!(build_ring(2, None));
assert_ok!(PeoplePallet::start_people_set_mutation_session());
let suspensions: &[PersonalId] = &[1, 2, 3, 4, 5, 11, 12, 13, 14, 15, 21, 22, 23, 24, 25];
assert_ok!(PeoplePallet::suspend_personhood(suspensions));
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1, 2, 3, 4, 5]);
assert_eq!(suspended_indices_list(1).into_inner(), vec![1, 2, 3, 4, 5]);
assert_eq!(suspended_indices_list(2).into_inner(), vec![1, 2, 3, 4, 5]);
assert_ok!(PeoplePallet::end_people_set_mutation_session());
RingsState::<Test>::put(RingMembersState::AppendOnly);
let poll_base_weight = <<Test as Config>::WeightInfo as crate::WeightInfo>::on_poll_base();
let idle_base_weight = <<Test as Config>::WeightInfo as crate::WeightInfo>::on_idle_base();
let step_remove_keys_weight =
<<Test as Config>::WeightInfo as crate::WeightInfo>::remove_suspended_people(
<Test as Config>::MaxRingSize::get(),
);
let step_pending_suspensions_weight =
<<Test as Config>::WeightInfo as crate::WeightInfo>::pending_suspensions_iteration();
let idle_step_weight = step_remove_keys_weight;
assert_eq!(PeoplePallet::on_idle(0, Weight::zero()), Weight::zero());
assert_eq!(suspended_indices_list(RI_ZERO).into_inner(), vec![1, 2, 3, 4, 5]);
assert_eq!(suspended_indices_list(1).into_inner(), vec![1, 2, 3, 4, 5]);
assert_eq!(suspended_indices_list(2).into_inner(), vec![1, 2, 3, 4, 5]);
let mut meter =
WeightMeter::with_limit(poll_base_weight + step_remove_keys_weight.saturating_mul(3));
PeoplePallet::on_poll(0, &mut meter);
assert_eq!(PendingSuspensions::<Test>::iter().count(), 2);
assert_eq!(meter.consumed(), poll_base_weight.saturating_add(step_remove_keys_weight));
let expected_idle_weight = idle_base_weight
.saturating_add(idle_step_weight.saturating_mul(2))
.saturating_add(step_pending_suspensions_weight.saturating_mul(2));
let actual_idle_weight = PeoplePallet::on_idle(0, expected_idle_weight.saturating_mul(2));
assert_eq!(PendingSuspensions::<Test>::iter().count(), 0);
assert_eq!(
actual_idle_weight,
idle_base_weight
.saturating_add(step_remove_keys_weight)
.saturating_add(
<<Test as Config>::WeightInfo as crate::WeightInfo>::remove_suspended_people(8)
)
.saturating_add(
<<Test as Config>::WeightInfo as crate::WeightInfo>::onboard_people()
)
.saturating_add(step_pending_suspensions_weight.saturating_mul(2))
);
});
}
#[test]
fn test_under_alias_revision_check() {
new_test_ext().execute_with(|| {
OnboardingSize::<Test>::set(1);
let (_, pk, sk) = generate_people_with_index(0, 0).pop().unwrap();
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(0, None));
let ring_info = Root::<Test>::get(0).expect("Ring must exist after building");
assert_eq!(ring_info.revision, 0);
let alias_account: u64 = 42;
setup_alias_account(&pk, &sk, MOCK_CONTEXT, alias_account);
let dummy_call = Box::new(RuntimeCall::from(frame_system::Call::remark { remark: vec![] }));
assert_ok!(PeoplePallet::under_alias(
RuntimeOrigin::signed(alias_account),
dummy_call.clone()
));
let mut ring_info = Root::<Test>::get(0).unwrap();
ring_info.revision = 1;
Root::<Test>::insert(0, ring_info);
assert_noop!(
PeoplePallet::under_alias(RuntimeOrigin::signed(alias_account), dummy_call),
sp_runtime::DispatchError::BadOrigin,
);
});
}
#[test]
fn resetting_alias_account_for_new_revision_is_refunded() {
new_test_ext().execute_with(|| {
use crate::pallet::Origin as PeopleOrigin;
OnboardingSize::<Test>::set(1);
let (_, _key, _secret) = generate_people_with_index(0, 0).pop().unwrap();
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(0, None));
let ring_info = Root::<Test>::get(0).expect("Ring must exist after build_ring");
assert_eq!(ring_info.revision, 0);
let account: u64 = 42;
let ca = ContextualAlias { alias: [1u8; 32], context: MOCK_CONTEXT };
let rev_ca = RevisedContextualAlias { revision: 0, ring: 0, ca: ca.clone() };
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalAlias(rev_ca.clone()));
let result = PeoplePallet::set_alias_account(origin, account, 0);
assert_eq!(result.unwrap(), frame_support::pallet_prelude::Pays::No.into());
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalAlias(rev_ca.clone()));
assert_noop!(
PeoplePallet::set_alias_account(origin, account, 0),
Error::<Test>::AliasAccountAlreadySet
);
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalAlias(rev_ca.clone()));
let account2: u64 = 43;
let result = PeoplePallet::set_alias_account(origin, account2, 0);
assert_eq!(result.unwrap(), frame_support::pallet_prelude::Pays::Yes.into());
let ring_info = Root::<Test>::get(0).unwrap();
Root::<Test>::insert(0, RingRoot { revision: 1, ..ring_info });
let rev_ca_new = RevisedContextualAlias { revision: 1, ring: 0, ca: ca.clone() };
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalAlias(rev_ca_new.clone()));
let result = PeoplePallet::set_alias_account(origin, account, 0);
assert_eq!(result.unwrap(), frame_support::pallet_prelude::Pays::No.into());
let ring_info = Root::<Test>::get(0).unwrap();
Root::<Test>::insert(1, ring_info);
let rev_ca_new = RevisedContextualAlias { revision: 1, ring: 1, ca };
let origin = RuntimeOrigin::from(PeopleOrigin::PersonalAlias(rev_ca_new.clone()));
let result = PeoplePallet::set_alias_account(origin, account, 0);
assert_eq!(result.unwrap(), frame_support::pallet_prelude::Pays::No.into());
});
}
#[test]
fn replay_protection_for_identity() {
new_test_ext().execute_with(|| {
const EXTENSION_VERSION: u8 = 0;
let alice_sec = Simple::new_secret([1u8; 32]);
let alice_pub = Simple::member_from_secret(&alice_sec);
let alice_index = PeoplePallet::reserve_new_id();
PeoplePallet::recognize_personhood(alice_index, Some(alice_pub)).unwrap();
let generate_setup_account_tx_ext_for_call = |call: RuntimeCall| {
let other_tx_ext = (frame_system::CheckNonce::<Test>::from(0),);
let msg = (&EXTENSION_VERSION, &call, &other_tx_ext)
.using_encoded(sp_io::hashing::blake2_256);
let signature = Simple::sign(&alice_sec, &msg).unwrap();
(
AsPerson::<Test>::new(Some(AsPersonInfo::AsPersonalIdentityWithProof(
signature,
alice_index,
))),
other_tx_ext.0,
)
};
let call = RuntimeCall::PeoplePallet(crate::Call::set_personal_id_account {
account: 10,
call_valid_at: System::block_number(),
});
let tx_ext = generate_setup_account_tx_ext_for_call(call.clone());
assert_ok!(exec_tx(None, tx_ext.clone(), call.clone()));
assert_eq!(crate::People::<Test>::get(alice_index).unwrap().account, Some(10));
assert_eq!(crate::AccountToPersonalId::<Test>::get(10), Some(alice_index));
assert_noop!(exec_tx(None, tx_ext.clone(), call.clone()), InvalidTransaction::Stale);
let call_2 = RuntimeCall::PeoplePallet(crate::Call::set_personal_id_account {
account: 11,
call_valid_at: System::block_number() + 1,
});
let tx_ext_2 = generate_setup_account_tx_ext_for_call(call_2.clone());
assert_noop!(exec_tx(None, tx_ext_2.clone(), call_2.clone()), InvalidTransaction::Future);
assert_eq!(crate::People::<Test>::get(alice_index).unwrap().account, Some(10));
assert_eq!(crate::AccountToPersonalId::<Test>::get(10), Some(alice_index));
assert_eq!(crate::AccountToPersonalId::<Test>::get(11), None);
mock::advance_to(System::block_number() + PeoplePallet::account_setup_time_tolerance());
assert_ok!(exec_tx(None, tx_ext_2.clone(), call_2.clone()));
assert_eq!(crate::People::<Test>::get(alice_index).unwrap().account, Some(11));
assert_eq!(crate::AccountToPersonalId::<Test>::get(11), Some(alice_index));
assert_eq!(crate::AccountToPersonalId::<Test>::get(10), None);
assert_ok!(exec_tx(None, tx_ext.clone(), call.clone()));
assert_eq!(crate::People::<Test>::get(alice_index).unwrap().account, Some(10));
assert_eq!(crate::AccountToPersonalId::<Test>::get(10), Some(alice_index));
assert_eq!(crate::AccountToPersonalId::<Test>::get(11), None);
assert_ok!(exec_tx(None, tx_ext_2.clone(), call_2.clone()));
assert_eq!(crate::People::<Test>::get(alice_index).unwrap().account, Some(11));
assert_eq!(crate::AccountToPersonalId::<Test>::get(11), Some(alice_index));
assert_eq!(crate::AccountToPersonalId::<Test>::get(10), None);
mock::advance_to(System::block_number() + 1);
assert_noop!(exec_tx(None, tx_ext, call), InvalidTransaction::Stale);
assert_eq!(crate::People::<Test>::get(alice_index).unwrap().account, Some(11));
assert_eq!(crate::AccountToPersonalId::<Test>::get(11), Some(alice_index));
assert_eq!(crate::AccountToPersonalId::<Test>::get(10), None);
});
}
#[test]
fn replay_protection_for_alias() {
new_test_ext().execute_with(|| {
const EXTENSION_VERSION: u8 = 0;
PeoplePallet::set_onboarding_size(RuntimeOrigin::root(), 1).unwrap();
let alice_sec = Simple::new_secret([1u8; 32]);
let alice_pub = Simple::member_from_secret(&alice_sec);
let alice_index = PeoplePallet::reserve_new_id();
PeoplePallet::recognize_personhood(alice_index, Some(alice_pub)).unwrap();
assert_ok!(PeoplePallet::onboard_people());
assert_ok!(build_ring(RI_ZERO, None));
let generate_alias_tx_ext_for_call = |call: RuntimeCall| {
let other_tx_ext = (frame_system::CheckNonce::<Test>::from(0),);
let msg = (&EXTENSION_VERSION, &call, &other_tx_ext)
.using_encoded(sp_io::hashing::blake2_256);
let commitment = Simple::open(&alice_pub, Some(alice_pub).into_iter()).unwrap();
let (proof, alias_value) =
Simple::create(commitment, &alice_sec, &MOCK_CONTEXT, &msg).unwrap();
let alias = ContextualAlias { context: MOCK_CONTEXT, alias: alias_value };
let tx_ext = (
AsPerson::<Test>::new(Some(AsPersonInfo::AsPersonalAliasWithProof(
proof,
0,
MOCK_CONTEXT,
))),
other_tx_ext.0,
);
(tx_ext, alias)
};
let call1 = RuntimeCall::PeoplePallet(crate::Call::set_alias_account {
account: 10,
call_valid_at: System::block_number(),
});
let (tx_ext1, alias) = generate_alias_tx_ext_for_call(call1.clone());
let rev_alias = RevisedContextualAlias { revision: 0, ring: 0, ca: alias.clone() };
assert_ok!(exec_tx(None, tx_ext1.clone(), call1.clone()));
assert_eq!(crate::AliasToAccount::<Test>::get(&alias), Some(10));
assert_eq!(crate::AccountToAlias::<Test>::get(10), Some(rev_alias.clone()));
assert_noop!(exec_tx(None, tx_ext1.clone(), call1.clone()), InvalidTransaction::Stale);
let call2 = RuntimeCall::PeoplePallet(crate::Call::set_alias_account {
account: 11,
call_valid_at: System::block_number() + 1,
});
let (tx_ext2, _) = generate_alias_tx_ext_for_call(call2.clone());
assert_noop!(exec_tx(None, tx_ext2.clone(), call2.clone()), InvalidTransaction::Future);
assert_eq!(crate::AliasToAccount::<Test>::get(&alias), Some(10));
assert_eq!(crate::AccountToAlias::<Test>::get(10), Some(rev_alias.clone()));
assert_eq!(crate::AccountToAlias::<Test>::get(11), None);
mock::advance_to(System::block_number() + PeoplePallet::account_setup_time_tolerance());
assert_ok!(exec_tx(None, tx_ext2.clone(), call2.clone()));
assert_eq!(crate::AliasToAccount::<Test>::get(&alias), Some(11));
assert_eq!(crate::AccountToAlias::<Test>::get(11), Some(rev_alias.clone()));
assert_eq!(crate::AccountToAlias::<Test>::get(10), None);
assert_ok!(exec_tx(None, tx_ext1.clone(), call1.clone()));
assert_eq!(crate::AliasToAccount::<Test>::get(&alias), Some(10));
assert_eq!(crate::AccountToAlias::<Test>::get(10), Some(rev_alias.clone()));
assert_eq!(crate::AccountToAlias::<Test>::get(11), None);
assert_ok!(exec_tx(None, tx_ext2.clone(), call2.clone()));
assert_eq!(crate::AliasToAccount::<Test>::get(&alias), Some(11));
assert_eq!(crate::AccountToAlias::<Test>::get(11), Some(rev_alias.clone()));
assert_eq!(crate::AccountToAlias::<Test>::get(10), None);
mock::advance_to(System::block_number() + 1);
assert_noop!(exec_tx(None, tx_ext1, call1), InvalidTransaction::Stale);
assert_eq!(crate::AliasToAccount::<Test>::get(&alias), Some(11));
assert_eq!(crate::AccountToAlias::<Test>::get(11), Some(rev_alias));
});
}