Files
pezkuwi-subxt/substrate/frame/identity/src/tests.rs
T
gupnik 7099f6e1b1 Removes as [disambiguation_path] from derive_impl usage (#3652)
Step in https://github.com/paritytech/polkadot-sdk/issues/171

This PR removes `as [disambiguation_path]` syntax from `derive_impl`
usage across the polkadot-sdk as introduced in
https://github.com/paritytech/polkadot-sdk/pull/3505
2024-03-15 07:46:09 +00:00

1661 lines
50 KiB
Rust

// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Tests for Identity Pallet
use super::*;
use crate::{
self as pallet_identity,
legacy::{IdentityField, IdentityInfo},
};
use codec::{Decode, Encode};
use frame_support::{
assert_noop, assert_ok, derive_impl, parameter_types,
traits::{ConstU32, ConstU64, Get, OnFinalize, OnInitialize},
BoundedVec,
};
use frame_system::EnsureRoot;
use sp_core::H256;
use sp_io::crypto::{sr25519_generate, sr25519_sign};
use sp_keystore::{testing::MemoryKeystore, KeystoreExt};
use sp_runtime::{
traits::{BadOrigin, BlakeTwo256, IdentifyAccount, IdentityLookup, Verify},
BuildStorage, MultiSignature, MultiSigner,
};
type AccountIdOf<Test> = <Test as frame_system::Config>::AccountId;
pub type AccountPublic = <MultiSignature as Verify>::Signer;
pub type AccountId = <AccountPublic as IdentifyAccount>::AccountId;
type Block = frame_system::mocking::MockBlock<Test>;
frame_support::construct_runtime!(
pub enum Test
{
System: frame_system,
Balances: pallet_balances,
Identity: pallet_identity,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type AccountData = pallet_balances::AccountData<u64>;
}
impl pallet_balances::Config for Test {
type Balance = u64;
type RuntimeEvent = RuntimeEvent;
type DustRemoval = ();
type ExistentialDeposit = ConstU64<1>;
type AccountStore = System;
type MaxLocks = ();
type MaxReserves = ();
type ReserveIdentifier = [u8; 8];
type WeightInfo = ();
type FreezeIdentifier = ();
type MaxFreezes = ();
type RuntimeHoldReason = ();
type RuntimeFreezeReason = ();
}
parameter_types! {
pub const MaxAdditionalFields: u32 = 2;
pub const MaxRegistrars: u32 = 20;
}
impl pallet_identity::Config for Test {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type Slashed = ();
type BasicDeposit = ConstU64<100>;
type ByteDeposit = ConstU64<10>;
type SubAccountDeposit = ConstU64<100>;
type MaxSubAccounts = ConstU32<2>;
type IdentityInformation = IdentityInfo<MaxAdditionalFields>;
type MaxRegistrars = MaxRegistrars;
type RegistrarOrigin = EnsureRoot<Self::AccountId>;
type ForceOrigin = EnsureRoot<Self::AccountId>;
type OffchainSignature = MultiSignature;
type SigningPublicKey = AccountPublic;
type UsernameAuthorityOrigin = EnsureRoot<Self::AccountId>;
type PendingUsernameExpiration = ConstU64<100>;
type MaxSuffixLength = ConstU32<7>;
type MaxUsernameLength = ConstU32<32>;
type WeightInfo = ();
}
pub fn new_test_ext() -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
pallet_balances::GenesisConfig::<Test> {
balances: vec![
(account(1), 100),
(account(2), 100),
(account(3), 100),
(account(10), 1000),
(account(20), 1000),
(account(30), 1000),
],
}
.assimilate_storage(&mut t)
.unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.register_extension(KeystoreExt::new(MemoryKeystore::new()));
ext.execute_with(|| System::set_block_number(1));
ext
}
fn run_to_block(n: u64) {
while System::block_number() < n {
Identity::on_finalize(System::block_number());
Balances::on_finalize(System::block_number());
System::on_finalize(System::block_number());
System::set_block_number(System::block_number() + 1);
System::on_initialize(System::block_number());
Balances::on_initialize(System::block_number());
Identity::on_initialize(System::block_number());
}
}
fn account(id: u8) -> AccountIdOf<Test> {
[id; 32].into()
}
fn account_from_u32(id: u32) -> AccountIdOf<Test> {
let mut buffer = [255u8; 32];
let id_bytes = id.to_le_bytes();
let id_size = id_bytes.len();
for ii in 0..buffer.len() / id_size {
let s = ii * id_size;
let e = s + id_size;
buffer[s..e].clone_from_slice(&id_bytes[..]);
}
buffer.into()
}
fn accounts() -> [AccountIdOf<Test>; 8] {
[
account(1),
account(2),
account(3),
account(4), // unfunded
account(10),
account(20),
account(30),
account(40), // unfunded
]
}
fn unfunded_accounts() -> [AccountIdOf<Test>; 2] {
[account(100), account(101)]
}
// First return value is a username that would be submitted as a parameter to the dispatchable. As
// in, it has no suffix attached. Second is a full BoundedVec username with suffix, which is what a
// user would need to sign.
fn test_username_of(int: Vec<u8>, suffix: Vec<u8>) -> (Vec<u8>, Username<Test>) {
let base = b"testusername";
let mut username = Vec::with_capacity(base.len() + int.len());
username.extend(base);
username.extend(int);
let mut bounded_username = Vec::with_capacity(username.len() + suffix.len() + 1);
bounded_username.extend(username.clone());
bounded_username.extend(b".");
bounded_username.extend(suffix);
let bounded_username = Username::<Test>::try_from(bounded_username)
.expect("test usernames should fit within bounds");
(username, bounded_username)
}
fn infoof_ten() -> IdentityInfo<MaxAdditionalFields> {
IdentityInfo {
display: Data::Raw(b"ten".to_vec().try_into().unwrap()),
legal: Data::Raw(b"The Right Ordinal Ten, Esq.".to_vec().try_into().unwrap()),
..Default::default()
}
}
fn infoof_twenty() -> IdentityInfo<MaxAdditionalFields> {
IdentityInfo {
display: Data::Raw(b"twenty".to_vec().try_into().unwrap()),
legal: Data::Raw(b"The Right Ordinal Twenty, Esq.".to_vec().try_into().unwrap()),
..Default::default()
}
}
fn id_deposit(id: &IdentityInfo<MaxAdditionalFields>) -> u64 {
let base_deposit: u64 = <<Test as Config>::BasicDeposit as Get<u64>>::get();
let byte_deposit: u64 = <<Test as Config>::ByteDeposit as Get<u64>>::get() *
TryInto::<u64>::try_into(id.encoded_size()).unwrap();
base_deposit + byte_deposit
}
#[test]
fn identity_fields_repr_works() {
// `IdentityField` sanity checks.
assert_eq!(IdentityField::Display as u64, 1 << 0);
assert_eq!(IdentityField::Legal as u64, 1 << 1);
assert_eq!(IdentityField::Web as u64, 1 << 2);
assert_eq!(IdentityField::Riot as u64, 1 << 3);
assert_eq!(IdentityField::Email as u64, 1 << 4);
assert_eq!(IdentityField::PgpFingerprint as u64, 1 << 5);
assert_eq!(IdentityField::Image as u64, 1 << 6);
assert_eq!(IdentityField::Twitter as u64, 1 << 7);
let fields = IdentityField::Legal |
IdentityField::Web |
IdentityField::Riot |
IdentityField::PgpFingerprint |
IdentityField::Twitter;
assert!(!fields.contains(IdentityField::Display));
assert!(fields.contains(IdentityField::Legal));
assert!(fields.contains(IdentityField::Web));
assert!(fields.contains(IdentityField::Riot));
assert!(!fields.contains(IdentityField::Email));
assert!(fields.contains(IdentityField::PgpFingerprint));
assert!(!fields.contains(IdentityField::Image));
assert!(fields.contains(IdentityField::Twitter));
// Ensure that the `u64` representation matches what we expect.
assert_eq!(
fields.bits(),
0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_10101110
);
}
#[test]
fn editing_subaccounts_should_work() {
new_test_ext().execute_with(|| {
let data = |x| Data::Raw(vec![x; 1].try_into().unwrap());
let [one, two, three, _, ten, twenty, _, _] = accounts();
assert_noop!(
Identity::add_sub(RuntimeOrigin::signed(ten.clone()), twenty.clone(), data(1)),
Error::<Test>::NoIdentity
);
let ten_info = infoof_ten();
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(ten.clone()),
Box::new(ten_info.clone())
));
let id_deposit = id_deposit(&ten_info);
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit);
let sub_deposit: u64 = <<Test as Config>::SubAccountDeposit as Get<u64>>::get();
// first sub account
assert_ok!(Identity::add_sub(RuntimeOrigin::signed(ten.clone()), one.clone(), data(1)));
assert_eq!(SuperOf::<Test>::get(one.clone()), Some((ten.clone(), data(1))));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - sub_deposit);
// second sub account
assert_ok!(Identity::add_sub(RuntimeOrigin::signed(ten.clone()), two.clone(), data(2)));
assert_eq!(SuperOf::<Test>::get(one.clone()), Some((ten.clone(), data(1))));
assert_eq!(SuperOf::<Test>::get(two.clone()), Some((ten.clone(), data(2))));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - 2 * sub_deposit);
// third sub account is too many
assert_noop!(
Identity::add_sub(RuntimeOrigin::signed(ten.clone()), three.clone(), data(3)),
Error::<Test>::TooManySubAccounts
);
// rename first sub account
assert_ok!(Identity::rename_sub(RuntimeOrigin::signed(ten.clone()), one.clone(), data(11)));
assert_eq!(SuperOf::<Test>::get(one.clone()), Some((ten.clone(), data(11))));
assert_eq!(SuperOf::<Test>::get(two.clone()), Some((ten.clone(), data(2))));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - 2 * sub_deposit);
// remove first sub account
assert_ok!(Identity::remove_sub(RuntimeOrigin::signed(ten.clone()), one.clone()));
assert_eq!(SuperOf::<Test>::get(one.clone()), None);
assert_eq!(SuperOf::<Test>::get(two.clone()), Some((ten.clone(), data(2))));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - sub_deposit);
// add third sub account
assert_ok!(Identity::add_sub(RuntimeOrigin::signed(ten.clone()), three.clone(), data(3)));
assert_eq!(SuperOf::<Test>::get(one), None);
assert_eq!(SuperOf::<Test>::get(two), Some((ten.clone(), data(2))));
assert_eq!(SuperOf::<Test>::get(three), Some((ten.clone(), data(3))));
assert_eq!(Balances::free_balance(ten), 1000 - id_deposit - 2 * sub_deposit);
});
}
#[test]
fn resolving_subaccount_ownership_works() {
new_test_ext().execute_with(|| {
let data = |x| Data::Raw(vec![x; 1].try_into().unwrap());
let [one, _, _, _, ten, twenty, _, _] = accounts();
let sub_deposit: u64 = <<Test as Config>::SubAccountDeposit as Get<u64>>::get();
let ten_info = infoof_ten();
let ten_deposit = id_deposit(&ten_info);
let twenty_info = infoof_twenty();
let twenty_deposit = id_deposit(&twenty_info);
assert_ok!(Identity::set_identity(RuntimeOrigin::signed(ten.clone()), Box::new(ten_info)));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - ten_deposit);
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(twenty.clone()),
Box::new(twenty_info)
));
assert_eq!(Balances::free_balance(twenty.clone()), 1000 - twenty_deposit);
// 10 claims 1 as a subaccount
assert_ok!(Identity::add_sub(RuntimeOrigin::signed(ten.clone()), one.clone(), data(1)));
assert_eq!(Balances::free_balance(one.clone()), 100);
assert_eq!(Balances::free_balance(ten.clone()), 1000 - ten_deposit - sub_deposit);
assert_eq!(Balances::reserved_balance(ten.clone()), ten_deposit + sub_deposit);
// 20 cannot claim 1 now
assert_noop!(
Identity::add_sub(RuntimeOrigin::signed(twenty.clone()), one.clone(), data(1)),
Error::<Test>::AlreadyClaimed
);
// 1 wants to be with 20 so it quits from 10
assert_ok!(Identity::quit_sub(RuntimeOrigin::signed(one.clone())));
// 1 gets the 10 that 10 paid.
assert_eq!(Balances::free_balance(one.clone()), 100 + sub_deposit);
assert_eq!(Balances::free_balance(ten.clone()), 1000 - ten_deposit - sub_deposit);
assert_eq!(Balances::reserved_balance(ten), ten_deposit);
// 20 can claim 1 now
assert_ok!(Identity::add_sub(RuntimeOrigin::signed(twenty), one, data(1)));
});
}
#[test]
fn trailing_zeros_decodes_into_default_data() {
let encoded = Data::Raw(b"Hello".to_vec().try_into().unwrap()).encode();
assert!(<(Data, Data)>::decode(&mut &encoded[..]).is_err());
let input = &mut &encoded[..];
let (a, b) = <(Data, Data)>::decode(&mut AppendZerosInput::new(input)).unwrap();
assert_eq!(a, Data::Raw(b"Hello".to_vec().try_into().unwrap()));
assert_eq!(b, Data::None);
}
#[test]
fn adding_registrar_invalid_index() {
new_test_ext().execute_with(|| {
let [_, _, three, _, _, _, _, _] = accounts();
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), three.clone()));
assert_ok!(Identity::set_fee(RuntimeOrigin::signed(three.clone()), 0, 10));
let fields = IdentityField::Display | IdentityField::Legal;
assert_noop!(
Identity::set_fields(RuntimeOrigin::signed(three), 100, fields.bits()),
Error::<Test>::InvalidIndex
);
});
}
#[test]
fn adding_registrar_should_work() {
new_test_ext().execute_with(|| {
let [_, _, three, _, _, _, _, _] = accounts();
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), three.clone()));
assert_ok!(Identity::set_fee(RuntimeOrigin::signed(three.clone()), 0, 10));
let fields = IdentityField::Display | IdentityField::Legal;
assert_ok!(Identity::set_fields(RuntimeOrigin::signed(three.clone()), 0, fields.bits()));
assert_eq!(
Identity::registrars(),
vec![Some(RegistrarInfo { account: three, fee: 10, fields: fields.bits() })]
);
});
}
#[test]
fn amount_of_registrars_is_limited() {
new_test_ext().execute_with(|| {
for ii in 1..MaxRegistrars::get() + 1 {
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), account_from_u32(ii)));
}
let last_registrar = MaxRegistrars::get() + 1;
assert_noop!(
Identity::add_registrar(RuntimeOrigin::root(), account_from_u32(last_registrar)),
Error::<Test>::TooManyRegistrars
);
});
}
#[test]
fn registration_should_work() {
new_test_ext().execute_with(|| {
let [_, _, three, _, ten, _, _, _] = accounts();
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), three.clone()));
assert_ok!(Identity::set_fee(RuntimeOrigin::signed(three.clone()), 0, 10));
let mut three_fields = infoof_ten();
three_fields.additional.try_push(Default::default()).unwrap();
three_fields.additional.try_push(Default::default()).unwrap();
assert!(three_fields.additional.try_push(Default::default()).is_err());
let ten_info = infoof_ten();
let id_deposit = id_deposit(&ten_info);
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(ten.clone()),
Box::new(ten_info.clone())
));
assert_eq!(Identity::identity(ten.clone()).unwrap().0.info, ten_info);
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit);
assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(ten.clone())));
assert_eq!(Balances::free_balance(ten.clone()), 1000);
assert_noop!(
Identity::clear_identity(RuntimeOrigin::signed(ten)),
Error::<Test>::NoIdentity
);
});
}
#[test]
fn uninvited_judgement_should_work() {
new_test_ext().execute_with(|| {
let [_, _, three, _, ten, _, _, _] = accounts();
assert_noop!(
Identity::provide_judgement(
RuntimeOrigin::signed(three.clone()),
0,
ten.clone(),
Judgement::Reasonable,
H256::random()
),
Error::<Test>::InvalidIndex
);
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), three.clone()));
assert_noop!(
Identity::provide_judgement(
RuntimeOrigin::signed(three.clone()),
0,
ten.clone(),
Judgement::Reasonable,
H256::random()
),
Error::<Test>::InvalidTarget
);
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(ten.clone()),
Box::new(infoof_ten())
));
assert_noop!(
Identity::provide_judgement(
RuntimeOrigin::signed(three.clone()),
0,
ten.clone(),
Judgement::Reasonable,
H256::random()
),
Error::<Test>::JudgementForDifferentIdentity
);
let identity_hash = BlakeTwo256::hash_of(&infoof_ten());
assert_noop!(
Identity::provide_judgement(
RuntimeOrigin::signed(ten.clone()),
0,
ten.clone(),
Judgement::Reasonable,
identity_hash
),
Error::<Test>::InvalidIndex
);
assert_noop!(
Identity::provide_judgement(
RuntimeOrigin::signed(three.clone()),
0,
ten.clone(),
Judgement::FeePaid(1),
identity_hash
),
Error::<Test>::InvalidJudgement
);
assert_ok!(Identity::provide_judgement(
RuntimeOrigin::signed(three.clone()),
0,
ten.clone(),
Judgement::Reasonable,
identity_hash
));
assert_eq!(Identity::identity(ten).unwrap().0.judgements, vec![(0, Judgement::Reasonable)]);
});
}
#[test]
fn clearing_judgement_should_work() {
new_test_ext().execute_with(|| {
let [_, _, three, _, ten, _, _, _] = accounts();
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), three.clone()));
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(ten.clone()),
Box::new(infoof_ten())
));
assert_ok!(Identity::provide_judgement(
RuntimeOrigin::signed(three.clone()),
0,
ten.clone(),
Judgement::Reasonable,
BlakeTwo256::hash_of(&infoof_ten())
));
assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(ten.clone())));
assert_eq!(Identity::identity(ten), None);
});
}
#[test]
fn killing_slashing_should_work() {
new_test_ext().execute_with(|| {
let [one, _, _, _, ten, _, _, _] = accounts();
let ten_info = infoof_ten();
let id_deposit = id_deposit(&ten_info);
assert_ok!(Identity::set_identity(RuntimeOrigin::signed(ten.clone()), Box::new(ten_info)));
assert_noop!(Identity::kill_identity(RuntimeOrigin::signed(one), ten.clone()), BadOrigin);
assert_ok!(Identity::kill_identity(RuntimeOrigin::root(), ten.clone()));
assert_eq!(Identity::identity(ten.clone()), None);
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit);
assert_noop!(
Identity::kill_identity(RuntimeOrigin::root(), ten),
Error::<Test>::NoIdentity
);
});
}
#[test]
fn setting_subaccounts_should_work() {
new_test_ext().execute_with(|| {
let [_, _, _, _, ten, twenty, thirty, forty] = accounts();
let ten_info = infoof_ten();
let id_deposit = id_deposit(&ten_info);
let sub_deposit: u64 = <<Test as Config>::SubAccountDeposit as Get<u64>>::get();
let mut subs = vec![(twenty.clone(), Data::Raw(vec![40; 1].try_into().unwrap()))];
assert_noop!(
Identity::set_subs(RuntimeOrigin::signed(ten.clone()), subs.clone()),
Error::<Test>::NotFound
);
assert_ok!(Identity::set_identity(RuntimeOrigin::signed(ten.clone()), Box::new(ten_info)));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit);
assert_ok!(Identity::set_subs(RuntimeOrigin::signed(ten.clone()), subs.clone()));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - sub_deposit);
assert_eq!(
Identity::subs_of(ten.clone()),
(sub_deposit, vec![twenty.clone()].try_into().unwrap())
);
assert_eq!(
Identity::super_of(twenty.clone()),
Some((ten.clone(), Data::Raw(vec![40; 1].try_into().unwrap())))
);
// push another item and re-set it.
subs.push((thirty.clone(), Data::Raw(vec![50; 1].try_into().unwrap())));
assert_ok!(Identity::set_subs(RuntimeOrigin::signed(ten.clone()), subs.clone()));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - 2 * sub_deposit);
assert_eq!(
Identity::subs_of(ten.clone()),
(2 * sub_deposit, vec![twenty.clone(), thirty.clone()].try_into().unwrap())
);
assert_eq!(
Identity::super_of(twenty.clone()),
Some((ten.clone(), Data::Raw(vec![40; 1].try_into().unwrap())))
);
assert_eq!(
Identity::super_of(thirty.clone()),
Some((ten.clone(), Data::Raw(vec![50; 1].try_into().unwrap())))
);
// switch out one of the items and re-set.
subs[0] = (forty.clone(), Data::Raw(vec![60; 1].try_into().unwrap()));
assert_ok!(Identity::set_subs(RuntimeOrigin::signed(ten.clone()), subs.clone()));
// no change in the balance
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - 2 * sub_deposit);
assert_eq!(
Identity::subs_of(ten.clone()),
(2 * sub_deposit, vec![forty.clone(), thirty.clone()].try_into().unwrap())
);
assert_eq!(Identity::super_of(twenty.clone()), None);
assert_eq!(
Identity::super_of(thirty.clone()),
Some((ten.clone(), Data::Raw(vec![50; 1].try_into().unwrap())))
);
assert_eq!(
Identity::super_of(forty.clone()),
Some((ten.clone(), Data::Raw(vec![60; 1].try_into().unwrap())))
);
// clear
assert_ok!(Identity::set_subs(RuntimeOrigin::signed(ten.clone()), vec![]));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit);
assert_eq!(Identity::subs_of(ten.clone()), (0, BoundedVec::default()));
assert_eq!(Identity::super_of(thirty.clone()), None);
assert_eq!(Identity::super_of(forty), None);
subs.push((twenty, Data::Raw(vec![40; 1].try_into().unwrap())));
assert_noop!(
Identity::set_subs(RuntimeOrigin::signed(ten), subs.clone()),
Error::<Test>::TooManySubAccounts
);
});
}
#[test]
fn clearing_account_should_remove_subaccounts_and_refund() {
new_test_ext().execute_with(|| {
let [_, _, _, _, ten, twenty, _, _] = accounts();
let ten_info = infoof_ten();
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(ten.clone()),
Box::new(ten_info.clone())
));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit(&ten_info));
assert_ok!(Identity::set_subs(
RuntimeOrigin::signed(ten.clone()),
vec![(twenty.clone(), Data::Raw(vec![40; 1].try_into().unwrap()))]
));
assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(ten.clone())));
assert_eq!(Balances::free_balance(ten), 1000);
assert!(Identity::super_of(twenty).is_none());
});
}
#[test]
fn killing_account_should_remove_subaccounts_and_not_refund() {
new_test_ext().execute_with(|| {
let [_, _, _, _, ten, twenty, _, _] = accounts();
let ten_info = infoof_ten();
let id_deposit = id_deposit(&ten_info);
let sub_deposit: u64 = <<Test as Config>::SubAccountDeposit as Get<u64>>::get();
assert_ok!(Identity::set_identity(RuntimeOrigin::signed(ten.clone()), Box::new(ten_info)));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit);
assert_ok!(Identity::set_subs(
RuntimeOrigin::signed(ten.clone()),
vec![(twenty.clone(), Data::Raw(vec![40; 1].try_into().unwrap()))]
));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - sub_deposit);
assert_ok!(Identity::kill_identity(RuntimeOrigin::root(), ten.clone()));
assert_eq!(Balances::free_balance(ten), 1000 - id_deposit - sub_deposit);
assert!(Identity::super_of(twenty).is_none());
});
}
#[test]
fn cancelling_requested_judgement_should_work() {
new_test_ext().execute_with(|| {
let [_, _, three, _, ten, _, _, _] = accounts();
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), three.clone()));
assert_ok!(Identity::set_fee(RuntimeOrigin::signed(three.clone()), 0, 10));
assert_noop!(
Identity::cancel_request(RuntimeOrigin::signed(ten.clone()), 0),
Error::<Test>::NoIdentity
);
let ten_info = infoof_ten();
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(ten.clone()),
Box::new(ten_info.clone())
));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit(&ten_info));
assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(ten.clone()), 0, 10));
assert_ok!(Identity::cancel_request(RuntimeOrigin::signed(ten.clone()), 0));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit(&ten_info));
assert_noop!(
Identity::cancel_request(RuntimeOrigin::signed(ten.clone()), 0),
Error::<Test>::NotFound
);
assert_ok!(Identity::provide_judgement(
RuntimeOrigin::signed(three),
0,
ten.clone(),
Judgement::Reasonable,
BlakeTwo256::hash_of(&ten_info)
));
assert_noop!(
Identity::cancel_request(RuntimeOrigin::signed(ten), 0),
Error::<Test>::JudgementGiven
);
});
}
#[test]
fn requesting_judgement_should_work() {
new_test_ext().execute_with(|| {
let [_, _, three, four, ten, _, _, _] = accounts();
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), three.clone()));
assert_ok!(Identity::set_fee(RuntimeOrigin::signed(three.clone()), 0, 10));
let ten_info = infoof_ten();
let id_deposit = id_deposit(&ten_info);
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(ten.clone()),
Box::new(ten_info.clone())
));
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit);
assert_noop!(
Identity::request_judgement(RuntimeOrigin::signed(ten.clone()), 0, 9),
Error::<Test>::FeeChanged
);
assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(ten.clone()), 0, 10));
// 10 for the judgement request and the deposit for the identity.
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - 10);
// Re-requesting won't work as we already paid.
assert_noop!(
Identity::request_judgement(RuntimeOrigin::signed(ten.clone()), 0, 10),
Error::<Test>::StickyJudgement
);
assert_ok!(Identity::provide_judgement(
RuntimeOrigin::signed(three.clone()),
0,
ten.clone(),
Judgement::Erroneous,
BlakeTwo256::hash_of(&ten_info)
));
// Registrar got their payment now.
// 100 initial balance and 10 for the judgement.
assert_eq!(Balances::free_balance(three.clone()), 100 + 10);
// Re-requesting still won't work as it's erroneous.
assert_noop!(
Identity::request_judgement(RuntimeOrigin::signed(ten.clone()), 0, 10),
Error::<Test>::StickyJudgement
);
// Requesting from a second registrar still works.
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), four));
assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(ten.clone()), 1, 10));
// Re-requesting after the judgement has been reduced works.
assert_ok!(Identity::provide_judgement(
RuntimeOrigin::signed(three),
0,
ten.clone(),
Judgement::OutOfDate,
BlakeTwo256::hash_of(&ten_info)
));
assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(ten), 0, 10));
});
}
#[test]
fn provide_judgement_should_return_judgement_payment_failed_error() {
new_test_ext().execute_with(|| {
let [_, _, three, _, ten, _, _, _] = accounts();
let ten_info = infoof_ten();
let id_deposit = id_deposit(&ten_info);
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), three.clone()));
assert_ok!(Identity::set_fee(RuntimeOrigin::signed(three.clone()), 0, 10));
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(ten.clone()),
Box::new(ten_info.clone())
));
assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(ten.clone()), 0, 10));
// 10 for the judgement request and the deposit for the identity.
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - 10);
// This forces judgement payment failed error
Balances::make_free_balance_be(&three, 0);
assert_noop!(
Identity::provide_judgement(
RuntimeOrigin::signed(three.clone()),
0,
ten.clone(),
Judgement::Erroneous,
BlakeTwo256::hash_of(&ten_info)
),
Error::<Test>::JudgementPaymentFailed
);
});
}
#[test]
fn field_deposit_should_work() {
new_test_ext().execute_with(|| {
let [_, _, three, _, ten, _, _, _] = accounts();
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), three.clone()));
assert_ok!(Identity::set_fee(RuntimeOrigin::signed(three), 0, 10));
let id = IdentityInfo {
additional: vec![
(
Data::Raw(b"number".to_vec().try_into().unwrap()),
Data::Raw(10u32.encode().try_into().unwrap()),
),
(
Data::Raw(b"text".to_vec().try_into().unwrap()),
Data::Raw(b"10".to_vec().try_into().unwrap()),
),
]
.try_into()
.unwrap(),
..Default::default()
};
let id_deposit = id_deposit(&id);
assert_ok!(Identity::set_identity(RuntimeOrigin::signed(ten.clone()), Box::new(id)));
assert_eq!(Balances::free_balance(ten), 1000 - id_deposit);
});
}
#[test]
fn setting_account_id_should_work() {
new_test_ext().execute_with(|| {
let [_, _, three, four, _, _, _, _] = accounts();
assert_ok!(Identity::add_registrar(RuntimeOrigin::root(), three.clone()));
// account 4 cannot change the first registrar's identity since it's owned by 3.
assert_noop!(
Identity::set_account_id(RuntimeOrigin::signed(four.clone()), 0, three.clone()),
Error::<Test>::InvalidIndex
);
// account 3 can, because that's the registrar's current account.
assert_ok!(Identity::set_account_id(RuntimeOrigin::signed(three.clone()), 0, four.clone()));
// account 4 can now, because that's their new ID.
assert_ok!(Identity::set_account_id(RuntimeOrigin::signed(four), 0, three));
});
}
#[test]
fn test_has_identity() {
new_test_ext().execute_with(|| {
let [_, _, _, _, ten, _, _, _] = accounts();
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(ten.clone()),
Box::new(infoof_ten())
));
assert!(Identity::has_identity(&ten, IdentityField::Display as u64));
assert!(Identity::has_identity(&ten, IdentityField::Legal as u64));
assert!(Identity::has_identity(
&ten,
IdentityField::Display as u64 | IdentityField::Legal as u64
));
assert!(!Identity::has_identity(
&ten,
IdentityField::Display as u64 | IdentityField::Legal as u64 | IdentityField::Web as u64
));
});
}
#[test]
fn reap_identity_works() {
new_test_ext().execute_with(|| {
let [_, _, _, _, ten, twenty, _, _] = accounts();
let ten_info = infoof_ten();
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(ten.clone()),
Box::new(ten_info.clone())
));
assert_ok!(Identity::set_subs(
RuntimeOrigin::signed(ten.clone()),
vec![(twenty.clone(), Data::Raw(vec![40; 1].try_into().unwrap()))]
));
// deposit is correct
let id_deposit = id_deposit(&ten_info);
let subs_deposit: u64 = <<Test as Config>::SubAccountDeposit as Get<u64>>::get();
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - subs_deposit);
// reap
assert_ok!(Identity::reap_identity(&ten));
// no identity or subs
assert!(Identity::identity(ten.clone()).is_none());
assert!(Identity::super_of(twenty).is_none());
// balance is unreserved
assert_eq!(Balances::free_balance(ten), 1000);
});
}
#[test]
fn poke_deposit_works() {
new_test_ext().execute_with(|| {
let [_, _, _, _, ten, twenty, _, _] = accounts();
let ten_info = infoof_ten();
// Set a custom registration with 0 deposit
IdentityOf::<Test>::insert::<
_,
(
Registration<u64, MaxRegistrars, IdentityInfo<MaxAdditionalFields>>,
Option<Username<Test>>,
),
>(
&ten,
(
Registration {
judgements: Default::default(),
deposit: Zero::zero(),
info: ten_info.clone(),
},
None::<Username<Test>>,
),
);
assert!(Identity::identity(ten.clone()).is_some());
// Set a sub with zero deposit
SubsOf::<Test>::insert::<_, (u64, BoundedVec<AccountIdOf<Test>, ConstU32<2>>)>(
&ten,
(0, vec![twenty.clone()].try_into().unwrap()),
);
SuperOf::<Test>::insert(&twenty, (&ten, Data::Raw(vec![1; 1].try_into().unwrap())));
// Balance is free
assert_eq!(Balances::free_balance(ten.clone()), 1000);
// poke
assert_ok!(Identity::poke_deposit(&ten));
// free balance reduced correctly
let id_deposit = id_deposit(&ten_info);
let subs_deposit: u64 = <<Test as Config>::SubAccountDeposit as Get<u64>>::get();
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit - subs_deposit);
// new registration deposit is 10
assert_eq!(
Identity::identity(&ten),
Some((
Registration {
judgements: Default::default(),
deposit: id_deposit,
info: infoof_ten()
},
None
))
);
// new subs deposit is 10 vvvvvvvvvvvv
assert_eq!(Identity::subs_of(ten), (subs_deposit, vec![twenty].try_into().unwrap()));
});
}
#[test]
fn poke_deposit_does_not_insert_new_subs_storage() {
new_test_ext().execute_with(|| {
let [_, _, _, _, ten, _, _, _] = accounts();
let ten_info = infoof_ten();
// Set a custom registration with 0 deposit
IdentityOf::<Test>::insert::<
_,
(
Registration<u64, MaxRegistrars, IdentityInfo<MaxAdditionalFields>>,
Option<Username<Test>>,
),
>(
&ten,
(
Registration {
judgements: Default::default(),
deposit: Zero::zero(),
info: ten_info.clone(),
},
None::<Username<Test>>,
),
);
assert!(Identity::identity(ten.clone()).is_some());
// Balance is free
assert_eq!(Balances::free_balance(ten.clone()), 1000);
// poke
assert_ok!(Identity::poke_deposit(&ten));
// free balance reduced correctly
let id_deposit = id_deposit(&ten_info);
assert_eq!(Balances::free_balance(ten.clone()), 1000 - id_deposit);
// new registration deposit is 10
assert_eq!(
Identity::identity(&ten),
Some((
Registration {
judgements: Default::default(),
deposit: id_deposit,
info: infoof_ten()
},
None
))
);
// No new subs storage item.
assert!(!SubsOf::<Test>::contains_key(&ten));
});
}
#[test]
fn adding_and_removing_authorities_should_work() {
new_test_ext().execute_with(|| {
let [authority, _] = unfunded_accounts();
let suffix: Vec<u8> = b"test".to_vec();
let allocation: u32 = 10;
// add
assert_ok!(Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
suffix.clone(),
allocation
));
assert_eq!(
UsernameAuthorities::<Test>::get(&authority),
Some(AuthorityPropertiesOf::<Test> {
suffix: suffix.clone().try_into().unwrap(),
allocation
})
);
// update allocation
assert_ok!(Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
suffix.clone(),
11u32
));
assert_eq!(
UsernameAuthorities::<Test>::get(&authority),
Some(AuthorityPropertiesOf::<Test> {
suffix: suffix.try_into().unwrap(),
allocation: 11
})
);
// remove
assert_ok!(Identity::remove_username_authority(RuntimeOrigin::root(), authority.clone(),));
assert!(UsernameAuthorities::<Test>::get(&authority).is_none());
});
}
#[test]
fn set_username_with_signature_without_existing_identity_should_work() {
new_test_ext().execute_with(|| {
// set up authority
let [authority, _] = unfunded_accounts();
let suffix: Vec<u8> = b"test".to_vec();
let allocation: u32 = 10;
assert_ok!(Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
suffix.clone(),
allocation
));
// set up username
let (username, username_to_sign) = test_username_of(b"42".to_vec(), suffix);
let encoded_username = Encode::encode(&username_to_sign.to_vec());
// set up user and sign message
let public = sr25519_generate(0.into(), None);
let who_account: AccountIdOf<Test> = MultiSigner::Sr25519(public).into_account().into();
let signature =
MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &encoded_username).unwrap());
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority),
who_account.clone(),
username.clone(),
Some(signature)
));
// Even though user has no balance and no identity, they get a default one for free.
assert_eq!(
Identity::identity(&who_account),
Some((
Registration {
judgements: Default::default(),
deposit: 0,
info: Default::default()
},
Some(username_to_sign.clone())
))
);
// Lookup from username to account works.
assert_eq!(
AccountOfUsername::<Test>::get::<&Username<Test>>(&username_to_sign),
Some(who_account)
);
});
}
#[test]
fn set_username_with_signature_with_existing_identity_should_work() {
new_test_ext().execute_with(|| {
// set up authority
let [authority, _] = unfunded_accounts();
let suffix: Vec<u8> = b"test".to_vec();
let allocation: u32 = 10;
assert_ok!(Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
suffix.clone(),
allocation
));
// set up username
let (username, username_to_sign) = test_username_of(b"42".to_vec(), suffix);
let encoded_username = Encode::encode(&username_to_sign.to_vec());
// set up user and sign message
let public = sr25519_generate(0.into(), None);
let who_account: AccountIdOf<Test> = MultiSigner::Sr25519(public).into_account().into();
let signature =
MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &encoded_username).unwrap());
// Set an identity for who. They need some balance though.
Balances::make_free_balance_be(&who_account, 1000);
let ten_info = infoof_ten();
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(who_account.clone()),
Box::new(ten_info.clone())
));
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority),
who_account.clone(),
username.clone(),
Some(signature)
));
assert_eq!(
Identity::identity(&who_account),
Some((
Registration {
judgements: Default::default(),
deposit: id_deposit(&ten_info),
info: ten_info
},
Some(username_to_sign.clone())
))
);
assert_eq!(
AccountOfUsername::<Test>::get::<&Username<Test>>(&username_to_sign),
Some(who_account)
);
});
}
#[test]
fn set_username_with_bytes_signature_should_work() {
new_test_ext().execute_with(|| {
// set up authority
let [authority, _] = unfunded_accounts();
let suffix: Vec<u8> = b"test".to_vec();
let allocation: u32 = 10;
assert_ok!(Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
suffix.clone(),
allocation
));
// set up user
let public = sr25519_generate(0.into(), None);
let who_account: AccountIdOf<Test> = MultiSigner::Sr25519(public).into_account().into();
// set up username
let (username, username_to_sign) = test_username_of(b"42".to_vec(), suffix);
let unwrapped_username = username_to_sign.to_vec();
// Sign an unwrapped version, as in `username.suffix`.
let unwrapped_encoded = Encode::encode(&unwrapped_username);
let signature_on_unwrapped =
MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &unwrapped_encoded).unwrap());
// Trivial
assert_ok!(Identity::validate_signature(
&unwrapped_encoded,
&signature_on_unwrapped,
&who_account
));
// Here we are going to wrap the username and suffix in "<Bytes>" and verify that the
// signature verification still works, but only the username gets set in storage.
let prehtml = b"<Bytes>";
let posthtml = b"</Bytes>";
let mut wrapped_username: Vec<u8> =
Vec::with_capacity(unwrapped_username.len() + prehtml.len() + posthtml.len());
wrapped_username.extend(prehtml);
wrapped_username.extend(unwrapped_encoded.clone());
wrapped_username.extend(posthtml);
let signature_on_wrapped =
MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &wrapped_username).unwrap());
// We want to call `validate_signature` on the *unwrapped* username, but the signature on
// the *wrapped* data.
assert_ok!(Identity::validate_signature(
&unwrapped_encoded,
&signature_on_wrapped,
&who_account
));
// Make sure it really works in context. Call `set_username_for` with the signature on the
// wrapped data.
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority),
who_account.clone(),
username,
Some(signature_on_wrapped)
));
// The username in storage should not include `<Bytes>`. As in, it's the original
// `username_to_sign`.
assert_eq!(
Identity::identity(&who_account),
Some((
Registration {
judgements: Default::default(),
deposit: 0,
info: Default::default()
},
Some(username_to_sign.clone())
))
);
// Likewise for the lookup.
assert_eq!(
AccountOfUsername::<Test>::get::<&Username<Test>>(&username_to_sign),
Some(who_account)
);
});
}
#[test]
fn set_username_with_acceptance_should_work() {
new_test_ext().execute_with(|| {
// set up authority
let [authority, who] = unfunded_accounts();
let suffix: Vec<u8> = b"test".to_vec();
let allocation: u32 = 10;
assert_ok!(Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
suffix.clone(),
allocation
));
// set up username
let (username, full_username) = test_username_of(b"101".to_vec(), suffix);
let now = frame_system::Pallet::<Test>::block_number();
let expiration = now + <<Test as Config>::PendingUsernameExpiration as Get<u64>>::get();
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority),
who.clone(),
username.clone(),
None
));
// Should be pending
assert_eq!(
PendingUsernames::<Test>::get::<&Username<Test>>(&full_username),
Some((who.clone(), expiration))
);
// Now the user can accept
assert_ok!(Identity::accept_username(
RuntimeOrigin::signed(who.clone()),
full_username.clone()
));
// No more pending
assert!(PendingUsernames::<Test>::get::<&Username<Test>>(&full_username).is_none());
// Check Identity storage
assert_eq!(
Identity::identity(&who),
Some((
Registration {
judgements: Default::default(),
deposit: 0,
info: Default::default()
},
Some(full_username.clone())
))
);
// Check reverse lookup
assert_eq!(AccountOfUsername::<Test>::get::<&Username<Test>>(&full_username), Some(who));
});
}
#[test]
fn invalid_usernames_should_be_rejected() {
new_test_ext().execute_with(|| {
let [authority, who] = unfunded_accounts();
let allocation: u32 = 10;
let valid_suffix = b"test".to_vec();
let invalid_suffixes = [
b"te.st".to_vec(), // not alphanumeric
b"su:ffx".to_vec(),
b"su_ffx".to_vec(),
b"Suffix".to_vec(), // capital
b"suffixes".to_vec(), // too long
];
for suffix in invalid_suffixes {
assert_noop!(
Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
suffix.clone(),
allocation
),
Error::<Test>::InvalidSuffix
);
}
// set a valid one now
assert_ok!(Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
valid_suffix,
allocation
));
// set up usernames
let invalid_usernames = [
b"TestUsername".to_vec(),
b"test_username".to_vec(),
b"test-username".to_vec(),
b"test:username".to_vec(),
b"test.username".to_vec(),
b"test@username".to_vec(),
b"test$username".to_vec(),
//0 1 2 v With `.test` this makes it too long.
b"testusernametestusernametest".to_vec(),
];
for username in invalid_usernames {
assert_noop!(
Identity::set_username_for(
RuntimeOrigin::signed(authority.clone()),
who.clone(),
username.clone(),
None
),
Error::<Test>::InvalidUsername
);
}
// valid one works
let valid_username = b"testusernametestusernametes".to_vec();
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority),
who,
valid_username,
None
));
});
}
#[test]
fn authorities_should_run_out_of_allocation() {
new_test_ext().execute_with(|| {
// set up authority
let [authority, _] = unfunded_accounts();
let [pi, e, c, _, _, _, _, _] = accounts();
let suffix: Vec<u8> = b"test".to_vec();
let allocation: u32 = 2;
assert_ok!(Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
suffix.clone(),
allocation
));
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority.clone()),
pi,
b"username314159".to_vec(),
None
));
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority.clone()),
e,
b"username271828".to_vec(),
None
));
assert_noop!(
Identity::set_username_for(
RuntimeOrigin::signed(authority.clone()),
c,
b"username299792458".to_vec(),
None
),
Error::<Test>::NoAllocation
);
});
}
#[test]
fn setting_primary_should_work() {
new_test_ext().execute_with(|| {
// set up authority
let [authority, _] = unfunded_accounts();
let suffix: Vec<u8> = b"test".to_vec();
let allocation: u32 = 10;
assert_ok!(Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
suffix.clone(),
allocation
));
// set up user
let public = sr25519_generate(0.into(), None);
let who_account: AccountIdOf<Test> = MultiSigner::Sr25519(public).into_account().into();
// set up username
let (first_username, first_to_sign) = test_username_of(b"42".to_vec(), suffix.clone());
let encoded_username = Encode::encode(&first_to_sign.to_vec());
let first_signature =
MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &encoded_username).unwrap());
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority.clone()),
who_account.clone(),
first_username.clone(),
Some(first_signature)
));
// First username set as primary.
assert_eq!(
Identity::identity(&who_account),
Some((
Registration {
judgements: Default::default(),
deposit: 0,
info: Default::default()
},
Some(first_to_sign.clone())
))
);
// set up username
let (second_username, second_to_sign) = test_username_of(b"101".to_vec(), suffix);
let encoded_username = Encode::encode(&second_to_sign.to_vec());
let second_signature =
MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &encoded_username).unwrap());
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority),
who_account.clone(),
second_username.clone(),
Some(second_signature)
));
// The primary is still the first username.
assert_eq!(
Identity::identity(&who_account),
Some((
Registration {
judgements: Default::default(),
deposit: 0,
info: Default::default()
},
Some(first_to_sign.clone())
))
);
// Lookup from both works.
assert_eq!(
AccountOfUsername::<Test>::get::<&Username<Test>>(&first_to_sign),
Some(who_account.clone())
);
assert_eq!(
AccountOfUsername::<Test>::get::<&Username<Test>>(&second_to_sign),
Some(who_account.clone())
);
assert_ok!(Identity::set_primary_username(
RuntimeOrigin::signed(who_account.clone()),
second_to_sign.clone()
));
// The primary is now the second username.
assert_eq!(
Identity::identity(&who_account),
Some((
Registration {
judgements: Default::default(),
deposit: 0,
info: Default::default()
},
Some(second_to_sign.clone())
))
);
// Lookup from both still works.
assert_eq!(
AccountOfUsername::<Test>::get::<&Username<Test>>(&first_to_sign),
Some(who_account.clone())
);
assert_eq!(
AccountOfUsername::<Test>::get::<&Username<Test>>(&second_to_sign),
Some(who_account)
);
});
}
#[test]
fn unaccepted_usernames_should_expire() {
new_test_ext().execute_with(|| {
// set up authority
let [authority, who] = unfunded_accounts();
let suffix: Vec<u8> = b"test".to_vec();
let allocation: u32 = 10;
assert_ok!(Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
suffix.clone(),
allocation
));
// set up username
let (username, full_username) = test_username_of(b"101".to_vec(), suffix);
let now = frame_system::Pallet::<Test>::block_number();
let expiration = now + <<Test as Config>::PendingUsernameExpiration as Get<u64>>::get();
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority),
who.clone(),
username.clone(),
None
));
// Should be pending
assert_eq!(
PendingUsernames::<Test>::get::<&Username<Test>>(&full_username),
Some((who.clone(), expiration))
);
run_to_block(now + expiration - 1);
// Cannot be removed
assert_noop!(
Identity::remove_expired_approval(
RuntimeOrigin::signed(account(1)),
full_username.clone()
),
Error::<Test>::NotExpired
);
run_to_block(now + expiration);
// Anyone can remove
assert_ok!(Identity::remove_expired_approval(
RuntimeOrigin::signed(account(1)),
full_username.clone()
));
// No more pending
assert!(PendingUsernames::<Test>::get::<&Username<Test>>(&full_username).is_none());
});
}
#[test]
fn removing_dangling_usernames_should_work() {
new_test_ext().execute_with(|| {
// set up authority
let [authority, caller] = unfunded_accounts();
let suffix: Vec<u8> = b"test".to_vec();
let allocation: u32 = 10;
assert_ok!(Identity::add_username_authority(
RuntimeOrigin::root(),
authority.clone(),
suffix.clone(),
allocation
));
// set up username
let (username, username_to_sign) = test_username_of(b"42".to_vec(), suffix.clone());
let encoded_username = Encode::encode(&username_to_sign.to_vec());
// set up user and sign message
let public = sr25519_generate(0.into(), None);
let who_account: AccountIdOf<Test> = MultiSigner::Sr25519(public).into_account().into();
let signature =
MultiSignature::Sr25519(sr25519_sign(0.into(), &public, &encoded_username).unwrap());
// Set an identity for who. They need some balance though.
Balances::make_free_balance_be(&who_account, 1000);
let ten_info = infoof_ten();
assert_ok!(Identity::set_identity(
RuntimeOrigin::signed(who_account.clone()),
Box::new(ten_info.clone())
));
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority.clone()),
who_account.clone(),
username.clone(),
Some(signature)
));
// Now they set up a second username.
let (username_two, username_two_to_sign) = test_username_of(b"43".to_vec(), suffix);
let encoded_username_two = Encode::encode(&username_two_to_sign.to_vec());
// set up user and sign message
let signature_two = MultiSignature::Sr25519(
sr25519_sign(0.into(), &public, &encoded_username_two).unwrap(),
);
assert_ok!(Identity::set_username_for(
RuntimeOrigin::signed(authority),
who_account.clone(),
username_two.clone(),
Some(signature_two)
));
// The primary should still be the first one.
assert_eq!(
Identity::identity(&who_account),
Some((
Registration {
judgements: Default::default(),
deposit: id_deposit(&ten_info),
info: ten_info
},
Some(username_to_sign.clone())
))
);
// But both usernames should look up the account.
assert_eq!(
AccountOfUsername::<Test>::get::<&Username<Test>>(&username_to_sign),
Some(who_account.clone())
);
assert_eq!(
AccountOfUsername::<Test>::get::<&Username<Test>>(&username_two_to_sign),
Some(who_account.clone())
);
// Someone tries to remove it, but they can't
assert_noop!(
Identity::remove_dangling_username(
RuntimeOrigin::signed(caller.clone()),
username_to_sign.clone()
),
Error::<Test>::InvalidUsername
);
// Now the user calls `clear_identity`
assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(who_account.clone()),));
// Identity is gone
assert!(Identity::identity(who_account.clone()).is_none());
// The reverse lookup of the primary is gone.
assert!(AccountOfUsername::<Test>::get::<&Username<Test>>(&username_to_sign).is_none());
// But the reverse lookup of the non-primary is still there
assert_eq!(
AccountOfUsername::<Test>::get::<&Username<Test>>(&username_two_to_sign),
Some(who_account)
);
// Now it can be removed
assert_ok!(Identity::remove_dangling_username(
RuntimeOrigin::signed(caller),
username_two_to_sign.clone()
));
// And the reverse lookup is gone
assert!(AccountOfUsername::<Test>::get::<&Username<Test>>(&username_two_to_sign).is_none());
});
}