mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-16 10:51:16 +00:00
7099f6e1b1
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
1661 lines
50 KiB
Rust
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());
|
|
});
|
|
}
|