1#![doc = include_str!("../README.md")]
11#![allow(clippy::expect_used)]
21#![cfg_attr(not(feature = "std"), no_std)]
22#![deny(
24 rustdoc::broken_intra_doc_links,
25 rustdoc::missing_crate_level_docs,
26 rustdoc::invalid_codeblock_attributes,
27 missing_docs
28)]
29extern crate alloc;
30
31use alloc::{string::String, vec};
32
33#[cfg(feature = "runtime-benchmarks")]
34mod benchmarking;
35
36use handles_utils::{
37 converter::{convert_to_canonical, split_display_name, HANDLE_DELIMITER},
38 suffix::generate_unique_suffixes,
39 validator::{
40 consists_of_supported_unicode_character_sets, contains_blocked_characters,
41 is_reserved_canonical_handle,
42 },
43};
44
45#[cfg(test)]
46mod tests;
47
48use alloc::vec::Vec;
49#[cfg(feature = "runtime-benchmarks")]
50use common_primitives::benchmarks::MsaBenchmarkHelper;
51use common_primitives::{
52 handles::*,
53 msa::{MessageSourceId, MsaLookup, MsaValidator},
54 node::EIP712Encode,
55};
56use frame_support::{dispatch::DispatchResult, ensure, pallet_prelude::*, traits::Get};
57use frame_system::pallet_prelude::*;
58use numtoa::*;
59pub use pallet::*;
60use sp_core::crypto::AccountId32;
61use sp_runtime::{traits::Convert, DispatchError, MultiSignature};
62
63pub mod handles_signed_extension;
64
65use common_runtime::signature::check_signature;
66
67pub mod weights;
68pub use weights::*;
69
70impl<T: Config> HandleProvider for Pallet<T> {
71 fn get_handle_for_msa(msa_id: MessageSourceId) -> Option<HandleResponse> {
72 Self::get_handle_for_msa(msa_id)
73 }
74}
75
76#[frame_support::pallet]
77pub mod pallet {
78
79 use handles_utils::trim_and_collapse_whitespace;
80
81 use super::*;
82 #[pallet::config]
83 pub trait Config: frame_system::Config {
84 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
86
87 type WeightInfo: WeightInfo;
89
90 type ConvertIntoAccountId32: Convert<Self::AccountId, AccountId32>;
92
93 type MsaInfoProvider: MsaLookup + MsaValidator<AccountId = Self::AccountId>;
95
96 #[pallet::constant]
98 type HandleSuffixMin: Get<SuffixRangeType>;
99
100 #[pallet::constant]
102 type HandleSuffixMax: Get<SuffixRangeType>;
103
104 #[pallet::constant]
106 type MortalityWindowSize: Get<u32>;
107
108 #[cfg(feature = "runtime-benchmarks")]
109 type MsaBenchmarkHelper: MsaBenchmarkHelper<Self::AccountId>;
111 }
112
113 #[pallet::pallet]
115 pub struct Pallet<T>(_);
116
117 #[pallet::storage]
120 pub type CanonicalBaseHandleAndSuffixToMSAId<T: Config> = StorageDoubleMap<
121 _,
122 Blake2_128Concat,
123 CanonicalBase,
124 Twox64Concat,
125 HandleSuffix,
126 MessageSourceId,
127 OptionQuery,
128 >;
129
130 #[pallet::storage]
133 pub type MSAIdToDisplayName<T: Config> = StorageMap<
134 _,
135 Twox64Concat,
136 MessageSourceId,
137 (DisplayHandle, BlockNumberFor<T>),
138 OptionQuery,
139 >;
140
141 #[pallet::storage]
146 pub type CanonicalBaseHandleToSuffixIndex<T: Config> = StorageMap<
147 _,
148 Blake2_128Concat,
149 CanonicalBase,
150 (SequenceIndex, SuffixRangeType),
151 OptionQuery,
152 >;
153
154 #[derive(PartialEq, Eq)] #[pallet::error]
156 pub enum Error<T> {
157 InvalidHandleEncoding,
159 InvalidHandleByteLength,
161 InvalidHandleCharacterLength,
163 HandleIsNotAllowed,
165 HandleContainsBlockedCharacters,
167 HandleDoesNotConsistOfSupportedCharacterSets,
169 SuffixesExhausted,
171 InvalidMessageSourceAccount,
173 InvalidSignature,
175 MSAHandleAlreadyExists,
177 MSAHandleDoesNotExist,
179 ProofHasExpired,
181 ProofNotYetValid,
183 HandleWithinMortalityPeriod,
185 InvalidHandle,
187 }
188
189 #[pallet::event]
190 #[pallet::generate_deposit(pub (super) fn deposit_event)]
191 pub enum Event<T: Config> {
192 HandleClaimed {
194 msa_id: MessageSourceId,
196 handle: Vec<u8>,
198 },
199 HandleRetired {
201 msa_id: MessageSourceId,
203 handle: Vec<u8>,
205 },
206 }
207
208 impl<T: Config> Pallet<T> {
209 pub fn get_next_suffix_index_for_canonical_handle(
226 canonical_base: CanonicalBase,
227 ) -> Result<SequenceIndex, DispatchError> {
228 let next: SequenceIndex;
229 match CanonicalBaseHandleToSuffixIndex::<T>::get(canonical_base) {
230 None => {
231 next = 0;
232 },
233 Some((current, suffix_min)) => {
234 let current_suffix_min = T::HandleSuffixMin::get();
235 if current_suffix_min != suffix_min {
237 next = 0;
238 } else {
239 next = current.checked_add(1).ok_or(Error::<T>::SuffixesExhausted)?;
240 }
241 },
242 }
243
244 Ok(next)
245 }
246
247 pub fn verify_signature_mortality(
261 signature_expires_at: BlockNumberFor<T>,
262 ) -> DispatchResult {
263 let current_block = frame_system::Pallet::<T>::block_number();
264
265 let mortality_period = Self::mortality_block_limit(current_block);
266 ensure!(mortality_period > signature_expires_at, Error::<T>::ProofNotYetValid);
267 ensure!(current_block < signature_expires_at, Error::<T>::ProofHasExpired);
268 Ok(())
269 }
270
271 pub fn verify_signed_payload<P>(
283 signature: &MultiSignature,
284 signer: &T::AccountId,
285 payload: &P,
286 ) -> DispatchResult
287 where
288 P: Encode + EIP712Encode,
289 {
290 let key = T::ConvertIntoAccountId32::convert(signer.clone());
291
292 ensure!(check_signature(signature, key, payload), Error::<T>::InvalidSignature);
293
294 Ok(())
295 }
296
297 pub fn verify_max_handle_byte_length(handle: Vec<u8>) -> DispatchResult {
306 ensure!(handle.len() as u32 <= HANDLE_BYTES_MAX, Error::<T>::InvalidHandleByteLength);
307 Ok(())
308 }
309
310 fn mortality_block_limit(current_block: BlockNumberFor<T>) -> BlockNumberFor<T> {
314 let mortality_size = T::MortalityWindowSize::get();
315 current_block + BlockNumberFor::<T>::from(mortality_size)
316 }
317 pub fn generate_suffix_for_canonical_handle(
330 canonical_base: &str,
331 cursor: usize,
332 ) -> Result<u16, DispatchError> {
333 let mut lazy_suffix_sequence = generate_unique_suffixes(
334 T::HandleSuffixMin::get(),
335 T::HandleSuffixMax::get(),
336 canonical_base,
337 );
338 match lazy_suffix_sequence.nth(cursor) {
339 Some(suffix) => Ok(suffix),
340 None => Err(Error::<T>::SuffixesExhausted.into()),
341 }
342 }
343 }
344
345 #[pallet::call]
346 impl<T: Config> Pallet<T> {
347 #[pallet::call_index(0)]
373 #[pallet::weight(T::WeightInfo::claim_handle(payload.base_handle.len() as u32))]
374 pub fn claim_handle(
375 origin: OriginFor<T>,
376 msa_owner_key: T::AccountId,
377 proof: MultiSignature,
378 payload: ClaimHandlePayload<BlockNumberFor<T>>,
379 ) -> DispatchResult {
380 let _ = ensure_signed(origin)?;
381
382 Self::verify_max_handle_byte_length(payload.base_handle.clone())?;
384
385 let msa_id = T::MsaInfoProvider::ensure_valid_msa_key(&msa_owner_key)
387 .map_err(|_| Error::<T>::InvalidMessageSourceAccount)?;
388
389 Self::verify_signature_mortality(payload.expiration)?;
391
392 Self::verify_signed_payload(&proof, &msa_owner_key, &payload)?;
394
395 let display_handle = Self::do_claim_handle(msa_id, payload)?;
396
397 Self::deposit_event(Event::HandleClaimed { msa_id, handle: display_handle.clone() });
398 Ok(())
399 }
400
401 #[pallet::call_index(1)]
418 #[pallet::weight((T::WeightInfo::retire_handle(), DispatchClass::Normal, Pays::No))]
419 pub fn retire_handle(origin: OriginFor<T>) -> DispatchResult {
420 let msa_owner_key = ensure_signed(origin)?;
421
422 let msa_id = T::MsaInfoProvider::ensure_valid_msa_key(&msa_owner_key)
424 .map_err(|_| Error::<T>::InvalidMessageSourceAccount)?;
425
426 let display_handle: Vec<u8> = Self::do_retire_handle(msa_id)?;
427
428 Self::deposit_event(Event::HandleRetired { msa_id, handle: display_handle });
429 Ok(())
430 }
431
432 #[pallet::call_index(2)]
459 #[pallet::weight(T::WeightInfo::change_handle(payload.base_handle.len() as u32))]
460 pub fn change_handle(
461 origin: OriginFor<T>,
462 msa_owner_key: T::AccountId,
463 proof: MultiSignature,
464 payload: ClaimHandlePayload<BlockNumberFor<T>>,
465 ) -> DispatchResult {
466 let _ = ensure_signed(origin)?;
467
468 Self::verify_max_handle_byte_length(payload.base_handle.clone())?;
470
471 let msa_id = T::MsaInfoProvider::ensure_valid_msa_key(&msa_owner_key)
473 .map_err(|_| Error::<T>::InvalidMessageSourceAccount)?;
474
475 Self::verify_signature_mortality(payload.expiration)?;
477
478 Self::verify_signed_payload(&proof, &msa_owner_key, &payload)?;
480
481 MSAIdToDisplayName::<T>::get(msa_id).ok_or(Error::<T>::MSAHandleDoesNotExist)?;
483
484 let old_display_handle: Vec<u8> = Self::do_retire_handle(msa_id)?;
486 Self::deposit_event(Event::HandleRetired { msa_id, handle: old_display_handle });
487
488 let display_handle = Self::do_claim_handle(msa_id, payload.clone())?;
489
490 Self::deposit_event(Event::HandleClaimed { msa_id, handle: display_handle.clone() });
491
492 Ok(())
493 }
494 }
495
496 impl<T: Config> Pallet<T> {
497 pub fn get_handle_for_msa(msa_id: MessageSourceId) -> Option<HandleResponse> {
508 let display_handle = match MSAIdToDisplayName::<T>::get(msa_id) {
509 Some((handle, _)) => handle,
510 _ => return None,
511 };
512 let display_handle_str = core::str::from_utf8(&display_handle)
514 .map_err(|_| Error::<T>::InvalidHandleEncoding)
515 .ok()?;
516
517 let (base_handle_str, suffix) = split_display_name(display_handle_str)?;
518 let base_handle = base_handle_str.as_bytes().to_vec();
519 let (_, canonical_base) =
521 Self::get_canonical_string_vec_from_base_handle(&base_handle_str);
522 Some(HandleResponse { base_handle, suffix, canonical_base: canonical_base.into() })
523 }
524
525 pub fn get_next_suffixes(
541 base_handle: BaseHandle,
542 count: u16,
543 ) -> PresumptiveSuffixesResponse {
544 let base_handle_str = core::str::from_utf8(&base_handle).unwrap_or_default();
545
546 let (canonical_handle_str, canonical_base) =
548 Self::get_canonical_string_vec_from_base_handle(base_handle_str);
549
550 let suffix_index =
551 Self::get_next_suffix_index_for_canonical_handle(canonical_base.clone())
552 .unwrap_or_default();
553
554 let lazy_suffix_sequence = generate_unique_suffixes(
555 T::HandleSuffixMin::get(),
556 T::HandleSuffixMax::get(),
557 &canonical_handle_str,
558 )
559 .enumerate();
560
561 let mut suffixes: Vec<HandleSuffix> = vec![];
562
563 for (i, suffix) in lazy_suffix_sequence {
564 if i >= suffix_index as usize && i < (suffix_index as usize + count as usize) {
565 suffixes.push(suffix);
566 }
567 if suffixes.len() == count as usize {
568 break;
569 }
570 }
571 PresumptiveSuffixesResponse { base_handle: base_handle.into(), suffixes }
572 }
573
574 pub fn check_handle(base_handle: Vec<u8>) -> CheckHandleResponse {
591 let valid = Self::validate_handle(base_handle.to_vec());
592
593 if !valid {
594 return CheckHandleResponse { base_handle, ..Default::default() };
595 }
596
597 let base_handle_str = core::str::from_utf8(&base_handle).unwrap_or_default();
598
599 let base_handle_trimmed = trim_and_collapse_whitespace(base_handle_str);
600
601 let (_canonical_handle_str, canonical_base) =
603 Self::get_canonical_string_vec_from_base_handle(base_handle_str);
604
605 let suffix_index =
606 Self::get_next_suffix_index_for_canonical_handle(canonical_base.clone())
607 .unwrap_or_default();
608
609 let suffixes_available = suffix_index < T::HandleSuffixMax::get();
610
611 CheckHandleResponse {
612 base_handle: base_handle_trimmed.into(),
613 suffix_index,
614 suffixes_available,
615 valid,
616 canonical_base: canonical_base.into(),
617 }
618 }
619
620 pub fn get_msa_id_for_handle(display_handle: DisplayHandle) -> Option<MessageSourceId> {
631 let display_handle_str = core::str::from_utf8(&display_handle).unwrap_or_default();
632 let (base_handle_str, suffix) = split_display_name(display_handle_str)?;
633 let (_, canonical_base) =
635 Self::get_canonical_string_vec_from_base_handle(&base_handle_str);
636 CanonicalBaseHandleAndSuffixToMSAId::<T>::get(canonical_base, suffix)
637 }
638
639 pub fn do_claim_handle(
652 msa_id: MessageSourceId,
653 payload: ClaimHandlePayload<BlockNumberFor<T>>,
654 ) -> Result<Vec<u8>, DispatchError> {
655 ensure!(
657 MSAIdToDisplayName::<T>::get(msa_id).is_none(),
658 Error::<T>::MSAHandleAlreadyExists
659 );
660
661 let base_handle_str = Self::validate_base_handle(payload.base_handle)?;
662
663 let (canonical_handle_str, canonical_base) =
665 Self::get_canonical_string_vec_from_base_handle(&base_handle_str);
666
667 Self::validate_canonical_handle(&canonical_handle_str)?;
668
669 let suffix_sequence_index =
671 Self::get_next_suffix_index_for_canonical_handle(canonical_base.clone())?;
672
673 let suffix = Self::generate_suffix_for_canonical_handle(
674 &canonical_handle_str,
675 suffix_sequence_index as usize,
676 )?;
677
678 let display_handle = Self::build_full_display_handle(&base_handle_str, suffix)?;
679
680 CanonicalBaseHandleAndSuffixToMSAId::<T>::insert(
682 canonical_base.clone(),
683 suffix,
684 msa_id,
685 );
686 CanonicalBaseHandleToSuffixIndex::<T>::set(
688 canonical_base.clone(),
689 Some((suffix_sequence_index, T::HandleSuffixMin::get())),
690 );
691
692 MSAIdToDisplayName::<T>::insert(msa_id, (display_handle.clone(), payload.expiration));
694
695 Ok(display_handle.into_inner())
696 }
697
698 fn validate_base_handle(base_handle: Vec<u8>) -> Result<String, DispatchError> {
700 let base_handle_str =
702 String::from_utf8(base_handle).map_err(|_| Error::<T>::InvalidHandleEncoding)?;
703
704 let len = base_handle_str.chars().count() as u32;
707
708 ensure!(
710 (HANDLE_CHARS_MIN..=HANDLE_CHARS_MAX).contains(&len),
711 Error::<T>::InvalidHandleCharacterLength
712 );
713
714 ensure!(
715 !contains_blocked_characters(&base_handle_str),
716 Error::<T>::HandleContainsBlockedCharacters
717 );
718 Ok(base_handle_str)
719 }
720
721 fn validate_canonical_handle(canonical_base_handle_str: &str) -> DispatchResult {
726 ensure!(
728 consists_of_supported_unicode_character_sets(canonical_base_handle_str),
729 Error::<T>::HandleDoesNotConsistOfSupportedCharacterSets
730 );
731 ensure!(
732 !is_reserved_canonical_handle(canonical_base_handle_str),
733 Error::<T>::HandleIsNotAllowed
734 );
735
736 let len = canonical_base_handle_str.chars().count() as u32;
737
738 ensure!(
740 (HANDLE_CHARS_MIN..=HANDLE_CHARS_MAX).contains(&len),
741 Error::<T>::InvalidHandleCharacterLength
742 );
743
744 Ok(())
745 }
746
747 pub fn build_full_display_handle(
759 base_handle: &str,
760 suffix: HandleSuffix,
761 ) -> Result<DisplayHandle, DispatchError> {
762 let base_handle_trimmed = trim_and_collapse_whitespace(base_handle);
763 let mut full_handle_vec: Vec<u8> = vec![];
764 full_handle_vec.extend(base_handle_trimmed.as_bytes());
765 full_handle_vec.push(HANDLE_DELIMITER as u8); let mut buff = [0u8; SUFFIX_MAX_DIGITS];
767 full_handle_vec.extend(suffix.numtoa(10, &mut buff));
768 let res = full_handle_vec.try_into().map_err(|_| Error::<T>::InvalidHandleEncoding)?;
769 Ok(res)
770 }
771
772 pub fn do_retire_handle(msa_id: MessageSourceId) -> Result<Vec<u8>, DispatchError> {
782 let handle_from_state =
784 MSAIdToDisplayName::<T>::get(msa_id).ok_or(Error::<T>::MSAHandleDoesNotExist)?;
785 let display_name_handle = handle_from_state.0;
786 let display_name_str = core::str::from_utf8(&display_name_handle)
787 .map_err(|_| Error::<T>::InvalidHandleEncoding)?;
788
789 let (base_handle_str, suffix_num) =
790 split_display_name(display_name_str).ok_or(Error::<T>::InvalidHandle)?;
791
792 let (_, canonical_base) =
794 Self::get_canonical_string_vec_from_base_handle(&base_handle_str);
795
796 MSAIdToDisplayName::<T>::remove(msa_id);
798 CanonicalBaseHandleAndSuffixToMSAId::<T>::remove(canonical_base, suffix_num);
799
800 Ok(display_name_str.as_bytes().to_vec())
801 }
802
803 pub fn validate_handle(handle: Vec<u8>) -> bool {
808 match Self::validate_base_handle(handle) {
809 Ok(base_handle_str) => {
810 let (canonical_handle_str, _) =
812 Self::get_canonical_string_vec_from_base_handle(&base_handle_str);
813
814 Self::validate_canonical_handle(&canonical_handle_str).is_ok()
815 },
816 _ => false,
817 }
818 }
819
820 fn get_canonical_string_vec_from_base_handle(
822 base_handle_str: &str,
823 ) -> (String, CanonicalBase) {
824 let canonical_handle_str = convert_to_canonical(base_handle_str);
825 let canonical_handle_vec = canonical_handle_str.as_bytes().to_vec();
826 let canonical_base: CanonicalBase = canonical_handle_vec.try_into().unwrap_or_default();
827 (canonical_handle_str, canonical_base)
828 }
829 }
830}