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 #[allow(deprecated)]
86 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
87
88 type WeightInfo: WeightInfo;
90
91 type ConvertIntoAccountId32: Convert<Self::AccountId, AccountId32>;
93
94 type MsaInfoProvider: MsaLookup + MsaValidator<AccountId = Self::AccountId>;
96
97 #[pallet::constant]
99 type HandleSuffixMin: Get<SuffixRangeType>;
100
101 #[pallet::constant]
103 type HandleSuffixMax: Get<SuffixRangeType>;
104
105 #[pallet::constant]
107 type MortalityWindowSize: Get<u32>;
108
109 #[cfg(feature = "runtime-benchmarks")]
110 type MsaBenchmarkHelper: MsaBenchmarkHelper<Self::AccountId>;
112 }
113
114 #[pallet::pallet]
116 pub struct Pallet<T>(_);
117
118 #[pallet::storage]
121 pub type CanonicalBaseHandleAndSuffixToMSAId<T: Config> = StorageDoubleMap<
122 _,
123 Blake2_128Concat,
124 CanonicalBase,
125 Twox64Concat,
126 HandleSuffix,
127 MessageSourceId,
128 OptionQuery,
129 >;
130
131 #[pallet::storage]
134 pub type MSAIdToDisplayName<T: Config> = StorageMap<
135 _,
136 Twox64Concat,
137 MessageSourceId,
138 (DisplayHandle, BlockNumberFor<T>),
139 OptionQuery,
140 >;
141
142 #[pallet::storage]
147 pub type CanonicalBaseHandleToSuffixIndex<T: Config> = StorageMap<
148 _,
149 Blake2_128Concat,
150 CanonicalBase,
151 (SequenceIndex, SuffixRangeType),
152 OptionQuery,
153 >;
154
155 #[derive(PartialEq, Eq)] #[pallet::error]
157 pub enum Error<T> {
158 InvalidHandleEncoding,
160 InvalidHandleByteLength,
162 InvalidHandleCharacterLength,
164 HandleIsNotAllowed,
166 HandleContainsBlockedCharacters,
168 HandleDoesNotConsistOfSupportedCharacterSets,
170 SuffixesExhausted,
172 InvalidMessageSourceAccount,
174 InvalidSignature,
176 MSAHandleAlreadyExists,
178 MSAHandleDoesNotExist,
180 ProofHasExpired,
182 ProofNotYetValid,
184 HandleWithinMortalityPeriod,
186 InvalidHandle,
188 }
189
190 #[pallet::event]
191 #[pallet::generate_deposit(pub (super) fn deposit_event)]
192 pub enum Event<T: Config> {
193 HandleClaimed {
195 msa_id: MessageSourceId,
197 handle: Vec<u8>,
199 },
200 HandleRetired {
202 msa_id: MessageSourceId,
204 handle: Vec<u8>,
206 },
207 }
208
209 impl<T: Config> Pallet<T> {
210 pub fn get_next_suffix_index_for_canonical_handle(
227 canonical_base: CanonicalBase,
228 ) -> Result<SequenceIndex, DispatchError> {
229 let next: SequenceIndex;
230 match CanonicalBaseHandleToSuffixIndex::<T>::get(canonical_base) {
231 None => {
232 next = 0;
233 },
234 Some((current, suffix_min)) => {
235 let current_suffix_min = T::HandleSuffixMin::get();
236 if current_suffix_min != suffix_min {
238 next = 0;
239 } else {
240 next = current.checked_add(1).ok_or(Error::<T>::SuffixesExhausted)?;
241 }
242 },
243 }
244
245 Ok(next)
246 }
247
248 pub fn verify_signature_mortality(
262 signature_expires_at: BlockNumberFor<T>,
263 ) -> DispatchResult {
264 let current_block = frame_system::Pallet::<T>::block_number();
265
266 let mortality_period = Self::mortality_block_limit(current_block);
267 ensure!(mortality_period > signature_expires_at, Error::<T>::ProofNotYetValid);
268 ensure!(current_block < signature_expires_at, Error::<T>::ProofHasExpired);
269 Ok(())
270 }
271
272 pub fn verify_signed_payload<P>(
284 signature: &MultiSignature,
285 signer: &T::AccountId,
286 payload: &P,
287 ) -> DispatchResult
288 where
289 P: Encode + EIP712Encode,
290 {
291 let key = T::ConvertIntoAccountId32::convert(signer.clone());
292
293 ensure!(check_signature(signature, key, payload), Error::<T>::InvalidSignature);
294
295 Ok(())
296 }
297
298 pub fn verify_max_handle_byte_length(handle: Vec<u8>) -> DispatchResult {
307 ensure!(handle.len() as u32 <= HANDLE_BYTES_MAX, Error::<T>::InvalidHandleByteLength);
308 Ok(())
309 }
310
311 fn mortality_block_limit(current_block: BlockNumberFor<T>) -> BlockNumberFor<T> {
315 let mortality_size = T::MortalityWindowSize::get();
316 current_block + BlockNumberFor::<T>::from(mortality_size)
317 }
318 pub fn generate_suffix_for_canonical_handle(
331 canonical_base: &str,
332 cursor: usize,
333 ) -> Result<u16, DispatchError> {
334 let mut lazy_suffix_sequence = generate_unique_suffixes(
335 T::HandleSuffixMin::get(),
336 T::HandleSuffixMax::get(),
337 canonical_base,
338 );
339 match lazy_suffix_sequence.nth(cursor) {
340 Some(suffix) => Ok(suffix),
341 None => Err(Error::<T>::SuffixesExhausted.into()),
342 }
343 }
344 }
345
346 #[pallet::call]
347 impl<T: Config> Pallet<T> {
348 #[pallet::call_index(0)]
374 #[pallet::weight(T::WeightInfo::claim_handle(payload.base_handle.len() as u32))]
375 pub fn claim_handle(
376 origin: OriginFor<T>,
377 msa_owner_key: T::AccountId,
378 proof: MultiSignature,
379 payload: ClaimHandlePayload<BlockNumberFor<T>>,
380 ) -> DispatchResult {
381 let _ = ensure_signed(origin)?;
382
383 Self::verify_max_handle_byte_length(payload.base_handle.clone())?;
385
386 let msa_id = T::MsaInfoProvider::ensure_valid_msa_key(&msa_owner_key)
388 .map_err(|_| Error::<T>::InvalidMessageSourceAccount)?;
389
390 Self::verify_signature_mortality(payload.expiration)?;
392
393 Self::verify_signed_payload(&proof, &msa_owner_key, &payload)?;
395
396 let display_handle = Self::do_claim_handle(msa_id, payload)?;
397
398 Self::deposit_event(Event::HandleClaimed { msa_id, handle: display_handle.clone() });
399 Ok(())
400 }
401
402 #[pallet::call_index(1)]
419 #[pallet::weight((T::WeightInfo::retire_handle(), DispatchClass::Normal, Pays::No))]
420 pub fn retire_handle(origin: OriginFor<T>) -> DispatchResult {
421 let msa_owner_key = ensure_signed(origin)?;
422
423 let msa_id = T::MsaInfoProvider::ensure_valid_msa_key(&msa_owner_key)
425 .map_err(|_| Error::<T>::InvalidMessageSourceAccount)?;
426
427 let display_handle: Vec<u8> = Self::do_retire_handle(msa_id)?;
428
429 Self::deposit_event(Event::HandleRetired { msa_id, handle: display_handle });
430 Ok(())
431 }
432
433 #[pallet::call_index(2)]
460 #[pallet::weight(T::WeightInfo::change_handle(payload.base_handle.len() as u32))]
461 pub fn change_handle(
462 origin: OriginFor<T>,
463 msa_owner_key: T::AccountId,
464 proof: MultiSignature,
465 payload: ClaimHandlePayload<BlockNumberFor<T>>,
466 ) -> DispatchResult {
467 let _ = ensure_signed(origin)?;
468
469 Self::verify_max_handle_byte_length(payload.base_handle.clone())?;
471
472 let msa_id = T::MsaInfoProvider::ensure_valid_msa_key(&msa_owner_key)
474 .map_err(|_| Error::<T>::InvalidMessageSourceAccount)?;
475
476 Self::verify_signature_mortality(payload.expiration)?;
478
479 Self::verify_signed_payload(&proof, &msa_owner_key, &payload)?;
481
482 MSAIdToDisplayName::<T>::get(msa_id).ok_or(Error::<T>::MSAHandleDoesNotExist)?;
484
485 let old_display_handle: Vec<u8> = Self::do_retire_handle(msa_id)?;
487 Self::deposit_event(Event::HandleRetired { msa_id, handle: old_display_handle });
488
489 let display_handle = Self::do_claim_handle(msa_id, payload.clone())?;
490
491 Self::deposit_event(Event::HandleClaimed { msa_id, handle: display_handle.clone() });
492
493 Ok(())
494 }
495 }
496
497 impl<T: Config> Pallet<T> {
498 pub fn get_handle_for_msa(msa_id: MessageSourceId) -> Option<HandleResponse> {
509 let display_handle = match MSAIdToDisplayName::<T>::get(msa_id) {
510 Some((handle, _)) => handle,
511 _ => return None,
512 };
513 let display_handle_str = core::str::from_utf8(&display_handle)
515 .map_err(|_| Error::<T>::InvalidHandleEncoding)
516 .ok()?;
517
518 let (base_handle_str, suffix) = split_display_name(display_handle_str)?;
519 let base_handle = base_handle_str.as_bytes().to_vec();
520 let (_, canonical_base) =
522 Self::get_canonical_string_vec_from_base_handle(&base_handle_str);
523 Some(HandleResponse { base_handle, suffix, canonical_base: canonical_base.into() })
524 }
525
526 pub fn get_next_suffixes(
542 base_handle: BaseHandle,
543 count: u16,
544 ) -> PresumptiveSuffixesResponse {
545 let base_handle_str = core::str::from_utf8(&base_handle).unwrap_or_default();
546
547 let (canonical_handle_str, canonical_base) =
549 Self::get_canonical_string_vec_from_base_handle(base_handle_str);
550
551 let suffix_index =
552 Self::get_next_suffix_index_for_canonical_handle(canonical_base.clone())
553 .unwrap_or_default();
554
555 let lazy_suffix_sequence = generate_unique_suffixes(
556 T::HandleSuffixMin::get(),
557 T::HandleSuffixMax::get(),
558 &canonical_handle_str,
559 )
560 .enumerate();
561
562 let mut suffixes: Vec<HandleSuffix> = vec![];
563
564 for (i, suffix) in lazy_suffix_sequence {
565 if i >= suffix_index as usize && i < (suffix_index as usize + count as usize) {
566 suffixes.push(suffix);
567 }
568 if suffixes.len() == count as usize {
569 break;
570 }
571 }
572 PresumptiveSuffixesResponse { base_handle: base_handle.into(), suffixes }
573 }
574
575 pub fn check_handle(base_handle: Vec<u8>) -> CheckHandleResponse {
592 let valid = Self::validate_handle(base_handle.to_vec());
593
594 if !valid {
595 return CheckHandleResponse { base_handle, ..Default::default() };
596 }
597
598 let base_handle_str = core::str::from_utf8(&base_handle).unwrap_or_default();
599
600 let base_handle_trimmed = trim_and_collapse_whitespace(base_handle_str);
601
602 let (_canonical_handle_str, canonical_base) =
604 Self::get_canonical_string_vec_from_base_handle(base_handle_str);
605
606 let suffix_index =
607 Self::get_next_suffix_index_for_canonical_handle(canonical_base.clone())
608 .unwrap_or_default();
609
610 let suffixes_available = suffix_index < T::HandleSuffixMax::get();
611
612 CheckHandleResponse {
613 base_handle: base_handle_trimmed.into(),
614 suffix_index,
615 suffixes_available,
616 valid,
617 canonical_base: canonical_base.into(),
618 }
619 }
620
621 pub fn get_msa_id_for_handle(display_handle: DisplayHandle) -> Option<MessageSourceId> {
632 let display_handle_str = core::str::from_utf8(&display_handle).unwrap_or_default();
633 let (base_handle_str, suffix) = split_display_name(display_handle_str)?;
634 let (_, canonical_base) =
636 Self::get_canonical_string_vec_from_base_handle(&base_handle_str);
637 CanonicalBaseHandleAndSuffixToMSAId::<T>::get(canonical_base, suffix)
638 }
639
640 pub fn do_claim_handle(
653 msa_id: MessageSourceId,
654 payload: ClaimHandlePayload<BlockNumberFor<T>>,
655 ) -> Result<Vec<u8>, DispatchError> {
656 ensure!(
658 MSAIdToDisplayName::<T>::get(msa_id).is_none(),
659 Error::<T>::MSAHandleAlreadyExists
660 );
661
662 let base_handle_str = Self::validate_base_handle(payload.base_handle)?;
663
664 let (canonical_handle_str, canonical_base) =
666 Self::get_canonical_string_vec_from_base_handle(&base_handle_str);
667
668 Self::validate_canonical_handle(&canonical_handle_str)?;
669
670 let suffix_sequence_index =
672 Self::get_next_suffix_index_for_canonical_handle(canonical_base.clone())?;
673
674 let suffix = Self::generate_suffix_for_canonical_handle(
675 &canonical_handle_str,
676 suffix_sequence_index as usize,
677 )?;
678
679 let display_handle = Self::build_full_display_handle(&base_handle_str, suffix)?;
680
681 CanonicalBaseHandleAndSuffixToMSAId::<T>::insert(
683 canonical_base.clone(),
684 suffix,
685 msa_id,
686 );
687 CanonicalBaseHandleToSuffixIndex::<T>::set(
689 canonical_base.clone(),
690 Some((suffix_sequence_index, T::HandleSuffixMin::get())),
691 );
692
693 MSAIdToDisplayName::<T>::insert(msa_id, (display_handle.clone(), payload.expiration));
695
696 Ok(display_handle.into_inner())
697 }
698
699 fn validate_base_handle(base_handle: Vec<u8>) -> Result<String, DispatchError> {
701 let base_handle_str =
703 String::from_utf8(base_handle).map_err(|_| Error::<T>::InvalidHandleEncoding)?;
704
705 let len = base_handle_str.chars().count() as u32;
708
709 ensure!(
711 (HANDLE_CHARS_MIN..=HANDLE_CHARS_MAX).contains(&len),
712 Error::<T>::InvalidHandleCharacterLength
713 );
714
715 ensure!(
716 !contains_blocked_characters(&base_handle_str),
717 Error::<T>::HandleContainsBlockedCharacters
718 );
719 Ok(base_handle_str)
720 }
721
722 fn validate_canonical_handle(canonical_base_handle_str: &str) -> DispatchResult {
727 ensure!(
729 consists_of_supported_unicode_character_sets(canonical_base_handle_str),
730 Error::<T>::HandleDoesNotConsistOfSupportedCharacterSets
731 );
732 ensure!(
733 !is_reserved_canonical_handle(canonical_base_handle_str),
734 Error::<T>::HandleIsNotAllowed
735 );
736
737 let len = canonical_base_handle_str.chars().count() as u32;
738
739 ensure!(
741 (HANDLE_CHARS_MIN..=HANDLE_CHARS_MAX).contains(&len),
742 Error::<T>::InvalidHandleCharacterLength
743 );
744
745 Ok(())
746 }
747
748 pub fn build_full_display_handle(
760 base_handle: &str,
761 suffix: HandleSuffix,
762 ) -> Result<DisplayHandle, DispatchError> {
763 let base_handle_trimmed = trim_and_collapse_whitespace(base_handle);
764 let mut full_handle_vec: Vec<u8> = vec![];
765 full_handle_vec.extend(base_handle_trimmed.as_bytes());
766 full_handle_vec.push(HANDLE_DELIMITER as u8); let mut buff = [0u8; SUFFIX_MAX_DIGITS];
768 full_handle_vec.extend(suffix.numtoa(10, &mut buff));
769 let res = full_handle_vec.try_into().map_err(|_| Error::<T>::InvalidHandleEncoding)?;
770 Ok(res)
771 }
772
773 pub fn do_retire_handle(msa_id: MessageSourceId) -> Result<Vec<u8>, DispatchError> {
783 let handle_from_state =
785 MSAIdToDisplayName::<T>::get(msa_id).ok_or(Error::<T>::MSAHandleDoesNotExist)?;
786 let display_name_handle = handle_from_state.0;
787 let display_name_str = core::str::from_utf8(&display_name_handle)
788 .map_err(|_| Error::<T>::InvalidHandleEncoding)?;
789
790 let (base_handle_str, suffix_num) =
791 split_display_name(display_name_str).ok_or(Error::<T>::InvalidHandle)?;
792
793 let (_, canonical_base) =
795 Self::get_canonical_string_vec_from_base_handle(&base_handle_str);
796
797 MSAIdToDisplayName::<T>::remove(msa_id);
799 CanonicalBaseHandleAndSuffixToMSAId::<T>::remove(canonical_base, suffix_num);
800
801 Ok(display_name_str.as_bytes().to_vec())
802 }
803
804 pub fn validate_handle(handle: Vec<u8>) -> bool {
809 match Self::validate_base_handle(handle) {
810 Ok(base_handle_str) => {
811 let (canonical_handle_str, _) =
813 Self::get_canonical_string_vec_from_base_handle(&base_handle_str);
814
815 Self::validate_canonical_handle(&canonical_handle_str).is_ok()
816 },
817 _ => false,
818 }
819 }
820
821 fn get_canonical_string_vec_from_base_handle(
823 base_handle_str: &str,
824 ) -> (String, CanonicalBase) {
825 let canonical_handle_str = convert_to_canonical(base_handle_str);
826 let canonical_handle_vec = canonical_handle_str.as_bytes().to_vec();
827 let canonical_base: CanonicalBase = canonical_handle_vec.try_into().unwrap_or_default();
828 (canonical_handle_str, canonical_base)
829 }
830 }
831}