Skip to content

Commit 9ffdc32

Browse files
authored
feat: new pallets into runtime (#784)
Part of KILTprotocol/ticket#3650, built on top of #782. I left a few comments on the review to help the reviewer understanding the context of this changeset. ## Checklist - [x] Add dotnames pallet to both runtimes - [x] Add second linking deployment with unique linking to both runtimes - [ ] ~Add new runtime API for batch resolution to both runtimes~ -> will do in a separate PR along with a new runtime API for unique linking resolution
1 parent 4199b6f commit 9ffdc32

22 files changed

+1651
-43
lines changed

pallets/pallet-migration/src/benchmarking.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ benchmarks! {
6666
<T as did::Config>::Currency: ReservableCurrency<AccountIdOf<T>, Balance = <<T as did::Config>::Currency as Inspect<AccountIdOf<T>>>::Balance>,
6767
<T as pallet_did_lookup::Config>::Currency: ReservableCurrency<AccountIdOf<T>,Balance = <<T as pallet_did_lookup::Config>::Currency as Inspect<AccountIdOf<T>>>::Balance>,
6868
<T as pallet_web3_names::Config>::Currency: ReservableCurrency<AccountIdOf<T>, Balance = <<T as pallet_web3_names::Config>::Currency as Inspect<AccountIdOf<T>>>::Balance>,
69+
<<T as pallet_web3_names::Config>::Web3Name as TryFrom<Vec<u8>>>::Error: Into<pallet_web3_names::Error<T>>,
6970
<T as public_credentials::Config>::Currency: ReservableCurrency<AccountIdOf<T>, Balance = <<T as public_credentials::Config>::Currency as Inspect<AccountIdOf<T>>>::Balance>,
7071
<T as did::Config>::DidIdentifier: From<AccountId32>,
7172
<T as frame_system::Config>::AccountId: From<AccountId32>,
@@ -188,7 +189,9 @@ benchmarks! {
188189
pallet_balances::Pallet::<T>::set_balance(&sender, KILT.saturated_into());
189190
pallet_web3_names::Pallet::<T>::claim(origin, web3_name_input.clone()).expect("Should register the claimed web3 name.");
190191
kilt_support::migration::translate_holds_to_reserve::<T>(pallet_web3_names::HoldReason::Deposit.into());
191-
let web3_name = Web3NameOf::<T>::try_from(web3_name_input.to_vec()).unwrap();
192+
let Ok(web3_name) = Web3NameOf::<T>::try_from(web3_name_input.to_vec()) else {
193+
panic!();
194+
};
192195

193196
let entries_to_migrate = EntriesToMigrate {
194197
w3n: BoundedVec::try_from(vec![web3_name]).expect("Vector initialization should not fail."),

pallets/pallet-web3-names/src/benchmarking.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use sp_std::{vec, vec::Vec};
3434
use kilt_support::{traits::GenerateBenchmarkOrigin, Deposit};
3535

3636
use crate::{
37-
mock::insert_raw_w3n, AccountIdOf, Banned, Call, Config, CurrencyOf, Names, Owner, Pallet, Web3NameOf,
37+
mock::insert_raw_w3n, AccountIdOf, Banned, Call, Config, CurrencyOf, Error, Names, Owner, Pallet, Web3NameOf,
3838
Web3NameOwnerOf,
3939
};
4040

@@ -64,6 +64,7 @@ benchmarks_instance_pallet! {
6464
T::Web3NameOwner: From<T::AccountId>,
6565
T::OwnerOrigin: GenerateBenchmarkOrigin<T::RuntimeOrigin, T::AccountId, T::Web3NameOwner>,
6666
T::BanOrigin: EnsureOrigin<T::RuntimeOrigin>,
67+
<<T as Config<I>>::Web3Name as TryFrom<Vec<u8>>>::Error: Into<Error<T, I>>,
6768
<T as Config<I>>::Currency: Mutate<T::AccountId>,
6869
}
6970

@@ -78,7 +79,9 @@ benchmarks_instance_pallet! {
7879
make_free_for_did::<T, I>(&caller);
7980
}: _<T::RuntimeOrigin>(origin, web3_name_input_clone)
8081
verify {
81-
let web3_name = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()).unwrap();
82+
let Ok(web3_name) = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()) else {
83+
panic!();
84+
};
8285
assert!(Names::<T, I>::get(&owner).is_some());
8386
assert!(Owner::<T, I>::get(&web3_name).is_some());
8487
}
@@ -93,7 +96,9 @@ benchmarks_instance_pallet! {
9396
Pallet::<T, I>::claim(origin.clone(), web3_name_input.clone()).expect("Should register the claimed web3 name.");
9497
}: _<T::RuntimeOrigin>(origin)
9598
verify {
96-
let web3_name = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()).unwrap();
99+
let Ok(web3_name) = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()) else {
100+
panic!();
101+
};
97102
assert!(Names::<T, I>::get(&owner).is_none());
98103
assert!(Owner::<T, I>::get(&web3_name).is_none());
99104
}
@@ -111,7 +116,9 @@ benchmarks_instance_pallet! {
111116
Pallet::<T, I>::claim(did_origin, web3_name_input.clone()).expect("Should register the claimed web3 name.");
112117
}: _(signed_origin, web3_name_input_clone)
113118
verify {
114-
let web3_name = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()).unwrap();
119+
let Ok(web3_name) = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()) else {
120+
panic!();
121+
};
115122
assert!(Names::<T, I>::get(&owner).is_none());
116123
assert!(Owner::<T, I>::get(&web3_name).is_none());
117124
}
@@ -129,7 +136,9 @@ benchmarks_instance_pallet! {
129136
Pallet::<T, I>::claim(did_origin, web3_name_input.clone()).expect("Should register the claimed web3 name.");
130137
}: _(ban_origin, web3_name_input_clone)
131138
verify {
132-
let web3_name = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()).unwrap();
139+
let Ok(web3_name) = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()) else {
140+
panic!();
141+
};
133142
assert!(Names::<T, I>::get(&owner).is_none());
134143
assert!(Owner::<T, I>::get(&web3_name).is_none());
135144
assert!(Banned::<T, I>::get(&web3_name).is_some());
@@ -147,7 +156,9 @@ benchmarks_instance_pallet! {
147156
Pallet::<T, I>::ban(ban_origin.clone().into(), web3_name_input.clone()).expect("Should ban the web3 name.");
148157
}: _(ban_origin, web3_name_input_clone)
149158
verify {
150-
let web3_name = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()).unwrap();
159+
let Ok(web3_name) = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()) else {
160+
panic!();
161+
};
151162
assert!(Names::<T, I>::get(&owner).is_none());
152163
assert!(Owner::<T, I>::get(&web3_name).is_none());
153164
assert!(Banned::<T, I>::get(&web3_name).is_none());
@@ -170,7 +181,9 @@ benchmarks_instance_pallet! {
170181
let origin = T::OwnerOrigin::generate_origin(deposit_owner_new.clone(), owner);
171182
}: _<T::RuntimeOrigin>(origin)
172183
verify {
173-
let web3_name = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()).unwrap();
184+
let Ok(web3_name) = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()) else {
185+
panic!();
186+
};
174187
assert_eq!(Owner::<T, I>::get(&web3_name).expect("w3n should exists").deposit, Deposit {
175188
owner: deposit_owner_new,
176189
amount: <T as Config<I>>::Deposit::get(),
@@ -183,7 +196,9 @@ benchmarks_instance_pallet! {
183196
let web3_name_input: BoundedVec<u8, T::MaxNameLength> = BoundedVec::try_from(
184197
generate_web3_name_input(T::MaxNameLength::get().saturated_into())
185198
).expect("BoundedVec creation should not fail.");
186-
let web3_name = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()).unwrap();
199+
let Ok(web3_name) = Web3NameOf::<T, I>::try_from(web3_name_input.to_vec()) else {
200+
panic!();
201+
};
187202

188203
make_free_for_did::<T, I>(&deposit_owner);
189204
insert_raw_w3n::<T, I>(

pallets/pallet-web3-names/src/lib.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,7 @@ pub mod pallet {
141141
// FIXME: Refactor the definition of AsciiWeb3Name so that we don't need to
142142
// require `Ord` here
143143
/// The type of a name.
144-
type Web3Name: FullCodec
145-
+ Debug
146-
+ PartialEq
147-
+ Clone
148-
+ TypeInfo
149-
+ TryFrom<Vec<u8>, Error = Error<Self, I>>
150-
+ MaxEncodedLen
151-
+ Ord;
144+
type Web3Name: FullCodec + Debug + PartialEq + Clone + TypeInfo + TryFrom<Vec<u8>> + MaxEncodedLen + Ord;
152145
/// The type of a name owner.
153146
type Web3NameOwner: Parameter + MaxEncodedLen;
154147
/// Weight information for extrinsics in this pallet.
@@ -225,7 +218,11 @@ pub mod pallet {
225218
}
226219

227220
#[pallet::call]
228-
impl<T: Config<I>, I: 'static> Pallet<T, I> {
221+
impl<T: Config<I>, I: 'static> Pallet<T, I>
222+
where
223+
// We introduce this trait bound instead of adding one more generic.
224+
<<T as Config<I>>::Web3Name as TryFrom<Vec<u8>>>::Error: Into<Error<T, I>>,
225+
{
229226
/// Assign the specified name to the owner as specified in the
230227
/// origin.
231228
///
@@ -399,7 +396,8 @@ pub mod pallet {
399396
#[pallet::weight(<T as Config<I>>::WeightInfo::update_deposit())]
400397
pub fn update_deposit(origin: OriginFor<T>, name_input: Web3NameInput<T, I>) -> DispatchResult {
401398
let source = ensure_signed(origin)?;
402-
let name = Web3NameOf::<T, I>::try_from(name_input.into_inner()).map_err(DispatchError::from)?;
399+
let name =
400+
Web3NameOf::<T, I>::try_from(name_input.into_inner()).map_err(|e| DispatchError::from(e.into()))?;
403401
let w3n_entry = Owner::<T, I>::get(&name).ok_or(Error::<T, I>::NotFound)?;
404402
ensure!(w3n_entry.deposit.owner == source, Error::<T, I>::NotAuthorized);
405403

@@ -409,7 +407,10 @@ pub mod pallet {
409407
}
410408
}
411409

412-
impl<T: Config<I>, I: 'static> Pallet<T, I> {
410+
impl<T: Config<I>, I: 'static> Pallet<T, I>
411+
where
412+
<<T as Config<I>>::Web3Name as TryFrom<Vec<u8>>>::Error: Into<Error<T, I>>,
413+
{
413414
/// Verify that the claiming preconditions are verified. Specifically:
414415
/// - The name input data can be decoded as a valid name
415416
/// - The name does not already exist
@@ -421,7 +422,8 @@ pub mod pallet {
421422
owner: &Web3NameOwnerOf<T, I>,
422423
deposit_payer: &AccountIdOf<T>,
423424
) -> Result<Web3NameOf<T, I>, DispatchError> {
424-
let name = Web3NameOf::<T, I>::try_from(name_input.into_inner()).map_err(DispatchError::from)?;
425+
let name =
426+
Web3NameOf::<T, I>::try_from(name_input.into_inner()).map_err(|e| DispatchError::from(e.into()))?;
425427

426428
ensure!(!Names::<T, I>::contains_key(owner), Error::<T, I>::OwnerAlreadyExists);
427429
ensure!(!Owner::<T, I>::contains_key(&name), Error::<T, I>::AlreadyExists);
@@ -487,7 +489,8 @@ pub mod pallet {
487489
name_input: Web3NameInput<T, I>,
488490
caller: &AccountIdOf<T>,
489491
) -> Result<Web3NameOf<T, I>, DispatchError> {
490-
let name = Web3NameOf::<T, I>::try_from(name_input.into_inner()).map_err(DispatchError::from)?;
492+
let name =
493+
Web3NameOf::<T, I>::try_from(name_input.into_inner()).map_err(|e| DispatchError::from(e.into()))?;
491494
let Web3NameOwnership { deposit, .. } = Owner::<T, I>::get(&name).ok_or(Error::<T, I>::NotFound)?;
492495

493496
ensure!(caller == &deposit.owner, Error::<T, I>::NotAuthorized);
@@ -534,7 +537,8 @@ pub mod pallet {
534537
fn check_banning_preconditions(
535538
name_input: Web3NameInput<T, I>,
536539
) -> Result<(Web3NameOf<T, I>, bool), DispatchError> {
537-
let name = Web3NameOf::<T, I>::try_from(name_input.into_inner()).map_err(DispatchError::from)?;
540+
let name =
541+
Web3NameOf::<T, I>::try_from(name_input.into_inner()).map_err(|e| DispatchError::from(e.into()))?;
538542

539543
ensure!(!Banned::<T, I>::contains_key(&name), Error::<T, I>::AlreadyBanned);
540544

@@ -555,7 +559,8 @@ pub mod pallet {
555559
/// - The name input data can be decoded as a valid name
556560
/// - The name must have already been banned
557561
fn check_unbanning_preconditions(name_input: Web3NameInput<T, I>) -> Result<Web3NameOf<T, I>, DispatchError> {
558-
let name = Web3NameOf::<T, I>::try_from(name_input.into_inner()).map_err(DispatchError::from)?;
562+
let name =
563+
Web3NameOf::<T, I>::try_from(name_input.into_inner()).map_err(|e| DispatchError::from(e.into()))?;
559564

560565
ensure!(Banned::<T, I>::contains_key(&name), Error::<T, I>::NotBanned);
561566

runtimes/common/src/constants.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,29 @@ pub mod web3_names {
507507
}
508508
}
509509

510+
pub mod dot_names {
511+
use super::*;
512+
513+
const MIN_NAME_LENGTH: u32 = 3;
514+
const MAX_NAME_LENGTH: u32 = 28;
515+
const DOT_NAME_SUFFIX: &str = ".dot";
516+
517+
#[allow(clippy::as_conversions)]
518+
pub const MIN_LENGTH: u32 = MIN_NAME_LENGTH + DOT_NAME_SUFFIX.len() as u32;
519+
#[allow(clippy::as_conversions)]
520+
pub const MAX_LENGTH: u32 = MAX_NAME_LENGTH + DOT_NAME_SUFFIX.len() as u32;
521+
522+
/// The size is checked in the runtime by a test.
523+
pub const MAX_NAME_BYTE_LENGTH: u32 = 121;
524+
pub const DEPOSIT: Balance = deposit(2, MAX_NAME_BYTE_LENGTH);
525+
526+
parameter_types! {
527+
pub const Web3NameDeposit: Balance = DEPOSIT;
528+
pub const MinNameLength: u32 = MIN_LENGTH;
529+
pub const MaxNameLength: u32 = MAX_LENGTH;
530+
}
531+
}
532+
510533
pub mod preimage {
511534
use super::*;
512535
parameter_types! {

runtimes/common/src/dip/did/mod.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ where
177177
+ pallet_balances::Config,
178178
<Runtime as frame_system::Config>::AccountId:
179179
Into<LinkableAccountId> + From<sp_core::sr25519::Public> + AsRef<[u8; 32]> + From<[u8; 32]>,
180+
<<Runtime as pallet_web3_names::Config>::Web3Name as TryFrom<Vec<u8>>>::Error:
181+
Into<pallet_web3_names::Error<Runtime>>,
180182
{
181183
type Output = Self;
182184

@@ -238,11 +240,15 @@ where
238240
let web3_name_input: BoundedVec<u8, <Runtime as pallet_web3_names::Config>::MaxNameLength> =
239241
BoundedVec::try_from(vec![b'1'; max_name_length]).expect("BoundedVec creation should not fail.");
240242

241-
let web3_name = pallet_web3_names::Web3NameOf::<Runtime>::try_from(web3_name_input.to_vec())
242-
.expect("Creation of w3n from w3n input should not fail.");
243+
let Ok(web3_name) = pallet_web3_names::Web3NameOf::<Runtime>::try_from(web3_name_input.to_vec()) else {
244+
panic!("Creation of w3n from w3n input should not fail.");
245+
};
243246

244-
pallet_web3_names::Pallet::<Runtime>::register_name(web3_name.clone(), did.clone(), submitter.clone())
245-
.expect("Inserting w3n into storage should not fail.");
247+
let Ok(_) =
248+
pallet_web3_names::Pallet::<Runtime>::register_name(web3_name.clone(), did.clone(), submitter.clone())
249+
else {
250+
panic!("Inserting w3n into storage should not fail.");
251+
};
246252

247253
let web3_name_details = Some(RevealedWeb3Name {
248254
web3_name,
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// KILT Blockchain – https://botlabs.org
2+
// Copyright (C) 2019-2024 BOTLabs GmbH
3+
4+
// The KILT Blockchain is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
9+
// The KILT Blockchain is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
// If you feel like getting in touch with us, you can do so at [email protected]
18+
19+
use frame_support::ensure;
20+
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
21+
use scale_info::TypeInfo;
22+
use sp_core::{ConstU32, RuntimeDebug};
23+
use sp_runtime::{BoundedVec, SaturatedConversion};
24+
use sp_std::vec::Vec;
25+
26+
use pallet_web3_names::{Config, Error};
27+
28+
#[cfg(test)]
29+
mod tests;
30+
31+
#[derive(Debug, Eq, PartialEq)]
32+
pub enum DotNameValidationError {
33+
TooShort,
34+
TooLong,
35+
InvalidCharacter,
36+
}
37+
38+
impl<T, I> From<DotNameValidationError> for Error<T, I>
39+
where
40+
T: Config<I>,
41+
I: 'static,
42+
{
43+
fn from(value: DotNameValidationError) -> Self {
44+
match value {
45+
DotNameValidationError::TooLong => Self::TooLong,
46+
DotNameValidationError::TooShort => Self::TooShort,
47+
DotNameValidationError::InvalidCharacter => Self::InvalidCharacter,
48+
}
49+
}
50+
}
51+
52+
#[derive(Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, PartialEq, Eq, PartialOrd, Ord, Clone)]
53+
pub struct DotName<const MIN_LENGTH: u32, const MAX_LENGTH: u32>(BoundedVec<u8, ConstU32<MAX_LENGTH>>);
54+
55+
impl<const MIN_LENGTH: u32, const MAX_LENGTH: u32> TryFrom<Vec<u8>> for DotName<MIN_LENGTH, MAX_LENGTH> {
56+
type Error = DotNameValidationError;
57+
58+
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
59+
ensure!(value.len() >= MIN_LENGTH.saturated_into(), Self::Error::TooShort);
60+
let bounded_vec: BoundedVec<u8, ConstU32<MAX_LENGTH>> =
61+
BoundedVec::try_from(value).map_err(|_| Self::Error::TooLong)?;
62+
ensure!(is_valid_dot_name(&bounded_vec), Self::Error::InvalidCharacter);
63+
Ok(Self(bounded_vec))
64+
}
65+
}
66+
fn is_valid_dot_name(input: &[u8]) -> bool {
67+
let Some(dot_name_without_suffix) = input.strip_suffix(b".dot") else {
68+
return false;
69+
};
70+
// Char validation logic taken from https://github.com/paritytech/polkadot-sdk/blob/657b5503a04e97737696fa7344641019350fb521/substrate/frame/identity/src/lib.rs#L1435
71+
dot_name_without_suffix
72+
.iter()
73+
.all(|c| c.is_ascii_digit() || c.is_ascii_lowercase())
74+
}

0 commit comments

Comments
 (0)